码迷,mamicode.com
首页 > 其他好文 > 详细

hdu 4297

时间:2015-10-15 20:22:47      阅读:177      评论:0      收藏:0      [点我收藏+]

标签:

有两个基础需要掌握:

RMQ,以及LCA。

RMQ:dp[i][j]表示下标从i开始,长度为2^j的一段元素中的最值。则易得状态转移如下:dp[i][j]=max/min(dp[i][j-1],dp[i+2^j-1][j-1];

 

LCA:最近公共祖先结点的求法:

可先进行一次dfs得到欧拉序列。

比如对,得到序列如下:

节点ver   1 3 1 2 5 7 5 6 5 2 4 2 1

深度R    1 2 1 2 3 4 3 4 3 2 3 2 1

首位first  1 4 2 11 5 8 6   在ver中第一次出现的位置。

则求LCA转化为求区间中深度最小的结点编号。

 

有,设最大结点数为N,则有

d[i]为结点i到根结点的距离。

技术分享
//邻接表建图,注意是无向图

int ver[N*2],R[N*2],first[N],vis[N],d[N];
void dfs(int u,int dep){
        vis[u]=1;
        ver[++tot]=u;
        first[u]=tot;
        R[tot]=dep;
        for(int i=head[u];~i;i=nxt[i]) if(!vis[to[i]]){
               int v=to[i];
               d[v]=d[u]+cost[i];
               dfs(v,dep+1);
               ver[++tot]=u;R[tot]=dep;
        }
}
void ST(int n){
        for(int i=1;i<=n;i++)
               dp[i][0]=i;
        for(int j=1;(1<<j)<=n;j++){
               for(int i=1;i+(1<<j)-1<=n;i++){
                       int a=dp[i][j-1],b=dp[i+(1<<(j-1))][j-1];
                       dp[i][j]=R[a]<R[b]?a:b;
               }
        }
}
int RMQ(int l,int r){
        int k=0;
        while((1<<(k+1))<=r-l+1) k++;
        int a=dp[l][k],b=dp[r-(1<<k)+1][k];
        return R[a]<R[b]?a:b;
}
int LCA(int u,int v){
        int x=first[u],y=first[v];
        if(x>y) swap(x,y);
        int res=RMQ(x,y);
        return ver[res];
}
View Code

 

则LCA(a,b)得到的就是结点a与b的最近公共祖先结点。

dfs从根结点开始调用。

 

对题hdu 4297。

题意:有n个房间,每个房间有且只有一条出边指向另一个房间。设两个人在不同的房间,求使得两人相遇所走的最少步数。

 

解题思路:

此处参考:http://blog.csdn.net/liuzhushiqiang/article/details/9916279

观察图的形态,由于n个点n条边,且每个点出度为1,因此可以认为是一个森林,森林里每棵树都加了一条边形成了一个环,且环是根节点(可以认为缩点后没有出边,即没有父节点)。

显然两点属于不同树的时候不可达;因为图的特殊性,因此没有用强连通做,而是用了并查集判环,也便于给环上每个点标上相对位置。

如果两点在同一颗树上,如果不在根节点的同一分支,那么他们首先要走到环上,然后一个人不动另一个走(至于让a走还是b走要根据两点在环上的相对位置判断优弧和劣弧),如果在同一分支,那就是普通的树上两点间的路径。

如果没有那个环,那就是一个普通的森林中的LCA问题,但是由于缩点了,因此要增加一个虚拟根节点0,把所有环上的点可做是森林中子树的根节点,虚拟根节点向所有树的根节点连边,然后做LCA,如果发现ab两点之间LCA为0,则判断两点是否在一颗子树中,如果不在和不可达,如果在则判优弧和劣弧;如果不等于0,那就等同于一棵树的LCA,可以直接确定两点间的最短距离。

技术分享
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=5e5+10,M=25;
int tot,ver[2*N],R[2*N],first[N],vis[N],d[N];
int head[N],to[N*2],nxt[N*2],cost[N*2],cnt;
int n,m,dp[2*N][M],F[N],tow[N],cc[N],root[N];
int num,pos[N],len[N];
void ini(){
    memset(vis,0,sizeof(vis));
    memset(head,-1,sizeof(head));
    memset(d,0,sizeof(d));
    memset(first,-1,sizeof(first));
    cnt=tot=0;
    memset(root,-1,sizeof(root));
}
void addedge(int u,int v,int w){
    to[cnt]=v;
    cost[cnt]=w;
    nxt[cnt]=head[u];
    head[u]=cnt++;
}
void dfs(int u,int dep){
    vis[u]=1;
    ver[++tot]=u;
    first[u]=tot;
    R[tot]=dep;
    for(int i=head[u];~i;i=nxt[i]) if(!vis[to[i]]){
        int v=to[i];
        d[v]=d[u]+cost[i];
        dfs(v,dep+1);
        ver[++tot]=u;R[tot]=dep;
    }
}
void dfs2(int u,int r){
    root[u]=r;
    for(int i=head[u];~i;i=nxt[i]) if(root[to[i]]==-1)
        dfs2(to[i],r);
}
void ST(int n){
    for(int i=1;i<=n;i++)
        dp[i][0]=i;
    for(int j=1;(1<<j)<=n;j++){
        for(int i=1;i+(1<<j)-1<=n;i++){
            int a=dp[i][j-1],b=dp[i+(1<<(j-1))][j-1];
            dp[i][j]=R[a]<R[b]?a:b;
        }
    }
}
int RMQ(int l,int r){
    int k=0;
    while((1<<(k+1))<=r-l+1) k++;
    int a=dp[l][k],b=dp[r-(1<<k)+1][k];
    return R[a]<R[b]?a:b;
}
int LCA(int u,int v){
    int x=first[u],y=first[v];
    if(x>y) swap(x,y);
    int res=RMQ(x,y);
    return ver[res];
}
int Find(int x){
    return x==F[x]?x:F[x]=Find(F[x]);
}
void circle(){
    for(int i=1;i<=n;i++)
        F[i]=i;
    num=0;
    memset(cc,-1,sizeof(cc));
    memset(pos,-1,sizeof(pos));
    for(int i=1;i<=n;i++){
        int u=Find(i),v=Find(tow[i]);
        if(u==v) cc[i]=++num;
        F[u]=v;
    }
    for(int i=1;i<=n;i++) if(cc[i]!=-1){
        int k=tow[i],tmp=0;
        pos[i]=++tmp;
        root[i]=i;
        while(k!=i){
            pos[k]=++tmp;
            root[k]=k;
            cc[k]=cc[i];
            k=tow[k];
        }
        len[cc[i]]=tmp;
    }
}
int main(){
    //freopen("in.txt","r",stdin);
    while(~scanf("%d%d",&n,&m)){
        ini();
        for(int i=1;i<=n;i++)
            scanf("%d",&tow[i]);
        circle();
        for(int i=1;i<=n;i++) if(root[i]!=i){
            addedge(tow[i],i,1);
            addedge(i,tow[i],1);
        }
        for(int i=1;i<=n;i++) if(root[i]==i){
            dfs2(i,i);
            addedge(0,i,0);
            addedge(i,0,0);
        }
        dfs(0,0);
        ST(2*n-1);
        int x,y;
        while(m--){
            scanf("%d%d",&x,&y);
            int p=LCA(x,y);
            if(p) printf("%d %d\n",d[x]-d[p],d[y]-d[p]);
            else{
                int rx=root[x],ry=root[y];
                if(cc[rx]!=cc[ry]) puts("-1 -1");
                else{
                    int sum=len[cc[rx]];
                    int d1=d[x],d2=d[y];
                    if(pos[rx]<pos[ry])
                        d1+=pos[ry]-pos[rx];
                    else d1+=(sum-pos[rx]+pos[ry]);

                    int d3=d[x],d4=d[y];
                    if(pos[ry]<pos[rx]) d4+=pos[rx]-pos[ry];
                    else d4+=(sum-pos[ry]+pos[rx]);

                    if(max(d1,d2)<max(d3,d4))
                        printf("%d %d\n",d1,d2);
                    else if(max(d1,d2)>max(d3,d4))
                        printf("%d %d\n",d3,d4);
                    else{
                        if(min(d1,d2)<min(d3,d4))
                            printf("%d %d\n",d1,d2);
                        else if(min(d1,d2)>min(d3,d4))
                            printf("%d %d\n",d3,d4);
                        else{
                            if(d1>=d2) printf("%d %d\n",d1,d2);
                            else printf("%d %d\n",d3,d4);
                        }
                    }
                }
            }
        }
    }
    return 0;
}
View Code

 

hdu 4297

标签:

原文地址:http://www.cnblogs.com/names-yc/p/4883411.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!