标签:
有两个基础需要掌握:
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]; }
则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; }
标签:
原文地址:http://www.cnblogs.com/names-yc/p/4883411.html