POJ 1984 Distance Queries LCA
2012-04-11 18:52
393 查看
http://poj.org/problem?id=1986
题意:给你一颗树,任意给定Q次询问(a,b) ,询问a 和b两个结点之间的距离。N<=40000 , Q<=40000
思路:LCA,因为是一颗树,我们可以任意选取一个结点作为树的根,然后用一次dfs,在O(E)的时间内求出所有点到根的距离,然后用LCA的Tarjin离线算法求出Q次询问的最近公共祖先,最后dis[i][j] = dis[i] + dis[j] - dis[ LCA(i,j) ] ;dis[i] 为i到根的距离。本题的方向可以无视。
实现一:Tarjin离线算法,复杂度为:O(N+Q) 219ms
代码:
实现二:dfs +rmq ,复杂度:O( 2*N*log(2*N) ) + O(1) 375ms
代码:
题意:给你一颗树,任意给定Q次询问(a,b) ,询问a 和b两个结点之间的距离。N<=40000 , Q<=40000
思路:LCA,因为是一颗树,我们可以任意选取一个结点作为树的根,然后用一次dfs,在O(E)的时间内求出所有点到根的距离,然后用LCA的Tarjin离线算法求出Q次询问的最近公共祖先,最后dis[i][j] = dis[i] + dis[j] - dis[ LCA(i,j) ] ;dis[i] 为i到根的距离。本题的方向可以无视。
实现一:Tarjin离线算法,复杂度为:O(N+Q) 219ms
代码:
#include<stdio.h> #include<string.h> #include<iostream> using namespace std; const int MAXN = 40010 ; int N , M ,Q; struct Node{ int num ,next ; int dis ; }edge[MAXN*2] ; int root[MAXN] , cnt ; int f[MAXN] ; int dis[MAXN] ; bool vis[MAXN] ; int p[MAXN] ; struct Node1{ int s,e ; int next ; int f ; }e2[10010*2] ; int root2[MAXN] ,c2 ; int ans[MAXN] ; void add(int a, int b , int c){ edge[cnt].num = b; edge[cnt].next = root[a] ; edge[cnt].dis = c ; root[a] = cnt++ ; } void Build(int u){ for(int i=root[u] ;i!=-1;i=edge[i].next){ int v = edge[i].num; if(v == f[u]) continue ; f[v] = u ; Build(v) ; } } void dfs(int u){ for(int i=root[u] ;i!=-1; i=edge[i].next){ int v = edge[i].num; if(v == f[u]) continue ; dis[v] = dis[u] + edge[i].dis ; dfs(v) ; } } void add2(int a ,int b ,int c){ e2[c2].e = b ; e2[c2].s = a ; e2[c2].next = root2[a] ; e2[c2].f = c ; root2[a] = c2++ ; } int find(int a){ if(a != p[a]){ p[a] = find( p[a] ) ; } return p[a] ; } void tarjin(int u){ vis[u] = 1; for(int i=root2[u] ;i!=-1;i=e2[i].next){ int v = e2[i].e ; int f = e2[i].f ; if(vis[v]){ ans[f] = find(v) ; } } for(int i=root[u] ;i!=-1;i=edge[i].next){ int v = edge[i].num ; if(v == f[u]) continue ; if(!vis[v]){ tarjin(v); p[v] = u ; } } } int main(){ int a,b ,c ,ff; char ch[4] ; while(scanf("%d %d",&N,&M) == 2){ memset(root , -1, sizeof(root)); cnt = 0 ; for(int i=1;i<=M;i++){ scanf("%d %d %d %s",&a,&b,&c,ch); add(a,b,c) ; add(b,a,c) ; } memset(f ,-1 ,sizeof(f)) ; Build(1); dis[1] = 0; dfs(1) ; memset(root2, -1 , sizeof(root2)); c2 = 0 ; scanf("%d",&Q); for(int i=1;i<=Q;i++){ scanf("%d %d",&a,&b); add2(a,b,i); add2(b,a,i) ; } memset(vis, 0 ,sizeof(vis)); for(int i=1;i<=N;i++) p[i] = i ; tarjin(1) ; for(int i=0;i<c2;i+=2){ a = e2[i].s ; b = e2[i].e ; ff = e2[i].f ; int res = dis[a] + dis[b] - 2 * dis[ ans[ff] ] ; printf("%d\n",res); } } return 0 ; }
实现二:dfs +rmq ,复杂度:O( 2*N*log(2*N) ) + O(1) 375ms
代码:
/* R[MAXN] : 每个结点第一次被遍历的下标 E[MAXN] : dfs序列 dep[MAXN] : dfs遍历结点深度 算法描述: (1)DFS:从树T的根开始,进行深度优先遍历,并记录下每次到达的顶点。 第一个的结点是root(T),每经过一条边都记录它的端点。由于每条边 恰好经过2次,因此一共记录了2n-1个结点,用E[1, ... , 2n-1]来表示。 (2)计算R:用R[i]表示E数组中第一个值为i的元素下标,即如果R[u] < R[v] 时,DFS访问的顺序是E[R[u], R[u]+1, ..., R[v]]。虽然其中包含u的后 代,但深度最小的还是u与v的公共祖先。 (3)RMQ:当R[u] ≥ R[v]时,LCA[T, u, v] = RMQ(L, R[v], R[u]);否 则LCA[T, u, v] = RMQ(L, R[u], R[v]),计算RMQ。由于RMQ中使用的ST 算法是在线算法,所以这个算法也是在线算法。 */ #include<stdio.h> #include<string.h> #include<algorithm> #include<math.h> #define MIN(a,b) (a)>(b)?(b):(a) const int MAXN = 40000+10 ; int N ,M , Q ; struct Edge{ int num,next,dis ;}edge[MAXN*2] ; int root[MAXN] , cnt ; bool vis[MAXN] ; int R[MAXN] , E[MAXN*2+1] ,dep[MAXN*2+1] ,idx; int dp[2*MAXN+1][22] ; int d[22] ,dis[MAXN]; void Init(){ memset(root , -1 ,sizeof(root)); memset( vis , 0,sizeof(vis) ); cnt = 0 ; } void add_edge(int a, int b ,int c){ edge[cnt].num = b ; edge[cnt].next = root[a] ; edge[cnt].dis = c ; root[a] = cnt++ ; } void dfs(int u ,int d){ vis[u] = 1 ; R[u] = idx ; E[idx] = u ; dep[idx++] = d ; for(int i=root[u] ;i!=-1;i=edge[i].next){ int v = edge[i].num; if(vis[v] == 0){ dfs(v, d+1); E[idx] = u ; dep[idx++] = d ; } } } void cal(int u){ vis[u] = 1; for(int i=root[u] ;i!=-1;i=edge[i].next){ int v= edge[i].num; int d = edge[i].dis ; if(vis[v] == 0){ dis[v] = dis[u] + d ; cal(v) ; } } } void Init_Rmq(){ int i , j ; for(d[0]=1,i=1;i<22;i++)d[i]=d[i-1]*2 ; for(i=1;i<idx;i++) dp[i][0] = i ; for(j=1;(1<<j)<idx;j++){ for(i=1;i+(1<<j)<idx;i++){ int idx1 = dp[i][j-1] ; int idx2 = dp[i+(1<<(j-1))][j-1] ; if(dep[ idx1 ] > dep[ idx2 ]){ dp[i][j] = idx2 ; } else{ dp[i][j] = idx1 ; } } } } int main(){ int i,j,a,b,c; char ch[5] ; while(scanf("%d %d",&N,&M) == 2){ Init() ; for(i=0;i<M;i++){ scanf("%d %d %d %s",&a,&b,&c,ch); add_edge(a,b,c); add_edge(b,a,c) ; } idx = 1 ; memset(vis , 0, sizeof(vis)); dis[1] = 0 ; cal(1); memset(vis , 0, sizeof(vis)); dfs(1,0); Init_Rmq() ; scanf("%d",&Q); for(i=0;i<Q;i++){ scanf("%d %d",&a,&b); int fa = R[a] ; int fb = R[b] ; if(fa > fb){ std::swap(fa, fb) ; } int k = int(log(double(fb-fa+1)) / log(2.0)) ; int idx1 = dp[ fa ][k] ; int idx2 = dp[ fb-(1<<k)+1 ][k] ; if(dep[ idx1 ] > dep[ idx2 ]) c = idx2 ; else c = idx1 ; printf("%d\n",dis[a]+dis[b]-2*dis[ E[c] ]); } } return 0 ; }
相关文章推荐
- POJ 1986 Distance Queries LCA
- poj 1968 Distance Queries LCA Tarjan 离线算法
- POJ 1986 Distance Queries LCA树上两点的距离
- Distance Queries POJ - 1986 (LCA在线方法求解 公共祖先(带权值))
- POJ 1986 Distance Queries && HDOJ 2586 How far away?
- POJ 1986 Distance Queries / UESTC 256 Distance Queries / CJOJ 1129 【USACO】距离咨询(最近公共祖先)
- 1986 Distance Queries //LCA
- 1986 Distance Queries //LCA+VECTOR 贼慢
- 【RMQ&LCA】Distance Queries(距…
- PKU 1986 Distance Queries LCA
- poj 2689 Prime Distance
- POJ 2689 - Prime Distance 任意区间内筛素数
- POJ 2688 Prime Distance
- POJ 2689 Prime Distance
- Poj 1659 Distance on Chessboard(国际象棋的走子规则)
- POJ-2689 Prime Distance (两重筛素数,区间平移)
- poj 3728 merchant 一个比较复杂的lca+dp
- poj 3417 Network LCA+树形dp
- POJ 2412:Equidistance 一道立体几何题
- poj 1470 Closest Common Ancestors tarjan求lca和树的孩子兄弟表示