TZOJ 4651 zry和他的的灯泡们(lca)
描述
zry有一个收集灯泡的习惯,他把灯泡的阴极都共地,阳极连成一颗树,这样的话,他只要在任意一个灯泡的阳极加上合适的电压,所有灯泡都能亮起来。不幸的是,有一对灯泡之间的阳极连线断掉了,这样的话,这颗灯泡树就还有一部分能亮,一部分不能亮了。zry想知道如果他在任意一个灯泡的阳极上加电压,这一对灯泡的哪一个会亮?
输入
首先是一个正整数T(1<=T<=10)表示测试数据的组数。
对于每一组测试数据:
第一行是一个整数n,q(3<=n,q<=100000),n表示灯泡总数,q表示查询个数。
接下来的n-1行,每行2个整数x,y(1<=x,y<=n),表示灯泡x和灯泡y的阳极相连。(数据保证合法,是一棵树)
接下来的q行,每行3个整数,a,b,c(1<=a,b,c<=n,数据保证合法,灯泡a和灯泡b之间有边且a不等于b)表示灯泡a和灯泡b之间阳极连线断开的话,在c的阳极加一个电压。
输出
每个查询之间相互独立,对于每个查询输出a和b哪一个会亮,输出a或者b即可。
样例输入
1
3 1
1 2
2 3
1 2 3
样例输出
2
题意
给一棵树,每次询问断掉一条边(a,b),问c和a还是b连通。
题解
不妨这么想,如果删了(a,b),那么c和两个点的距离哪个近就输出哪个。
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int N=1e5+5,L=18; 5 int dep[N],fa[N][L],lg[N],n; 6 bool vis[N]; 7 vector<int>G[N]; 8 void dfs(int u) 9 { 10 vis[u]=1; 11 for(int i=0;i<G[u].size();i++) 12 { 13 int v=G[u][i]; 14 if(vis[v])continue; 15 fa[v][0]=u; 16 dep[v]=dep[u]+1; 17 dfs(v); 18 } 19 } 20 void RMQ() 21 { 22 for(int j=1;(1<<j)<=n;j++) 23 for(int i=1;i<=n;i++) 24 fa[i][j]=fa[fa[i][j-1]][j-1]; 25 } 26 int lca(int x,int y) 27 { 28 if(dep[x]<dep[y])swap(x,y); 29 while(dep[x]>dep[y])x=fa[x][lg[dep[x]-dep[y]]-1]; 30 if(x==y)return x; 31 for(int k=lg[dep[x]];k>=0;k--) 32 if(fa[x][k]!=fa[y][k]) 33 x=fa[x][k],y=fa[y][k]; 34 return fa[x][0]; 35 } 36 int dist(int u,int v){return dep[u]+dep[v]-2*dep[lca(u, v)];} 37 int main() 38 { 39 for(int i=1;i<N;i++)lg[i]=lg[i-1]+(1<<lg[i-1]==i); 40 int t; 41 scanf("%d",&t); 42 while(t--) 43 { 44 int q; 45 scanf("%d%d",&n,&q); 46 for(int i=1;i<=n;i++)vis[i]=0; 47 for(int i=1;i<=n;i++)G[i].clear(); 48 for(int i=1,u,v;i<n;i++) 49 { 50 scanf("%d%d",&u,&v); 51 G[u].push_back(v); 52 G[v].push_back(u); 53 } 54 dfs(1); 55 RMQ(); 56 while(q--) 57 { 58 int a,b,c; 59 scanf("%d%d%d",&a,&b,&c); 60 int dis1=dist(a,c),dis2=dist(b,c); 61 if(dis1<dis2)printf("%d\n",a); 62 else printf("%d\n",b); 63 } 64 } 65 return 0; 66 }