HDU 2874"Connections between cities"(LCA+并查集+选根建树)
•题意
在一个包含 n 个节点 m 条边的森林中;
有 q 次询问,每次询问求解两点间的最短距离;
如果这两点不联通,输出 "Not connected";
•题解1
树上任意两点间的最短距离就是最近公共祖先分别到这两点的距离和;
那么这个问题就被转化成了LCA问题。
因为有多棵树,所以,对于每棵树,都提前预处理出 $dis,dep$;
并通过并查集判断询问的两点是否联通;
•Code
基于二分的LCA1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define mem(a,b) memset(a,b,sizeof(a)) 5 const int maxn=1e4+50; 6 7 int n,m,q; 8 int num; 9 int head[maxn]; 10 struct Edge 11 { 12 int to; 13 ll w; 14 int next; 15 }G[maxn<<1]; 16 void addEdge(int u,int v,ll w) 17 { 18 G[num]={v,w,head[u]}; 19 head[u]=num++; 20 } 21 vector<int >V[maxn]; 22 /** 23 fa[i][j]:节点j沿着其父结点向上走2^i步所到的节点(超过根节点时记为-1) 24 ///dis[i]:节点i的与根节点的距离 25 ///dep[i]:节点i的深度,根节点深度为0 26 */ 27 struct LCA 28 { 29 int fa[30][maxn]; 30 ll dis[maxn]; 31 ll dep[maxn]; 32 void DFS(int u,int f,ll Dis,ll Dep) 33 { 34 fa[0][u]=f;///节点u向上走2^0步来到的节点便是其父节点 35 dis[u]=Dis; 36 dep[u]=Dep; 37 for(int i=head[u];~i;i=G[i].next) 38 { 39 int v=G[i].to; 40 ll w=G[i].w; 41 if(v != f) 42 DFS(v,u,Dis+w,Dep+1); 43 } 44 } 45 void Init() 46 { 47 for(int i=1;i <= n;++i) 48 { 49 if(V[i].empty()) 50 continue; 51 ///预处理出每棵树的dis,dep,fa 52 DFS(V[i][0],-1,0,0); 53 for(int k=1;k <= 20;++k) 54 for(int j=0;j < V[i].size();++j) 55 { 56 int u=V[i][j]; 57 if(fa[k-1][u] == -1) 58 fa[k][u]=-1; 59 else 60 fa[k][u]=fa[k-1][fa[k-1][u]]; 61 } 62 } 63 } 64 int lca(int u,int v)///返回u,v的最近公共祖先 65 { 66 if(dep[u] > dep[v]) 67 swap(u,v); 68 69 for(int i=0;i <= 20;++i) 70 if((dep[v]-dep[u])>>i&1) 71 v=fa[i][v]; 72 if(u == v) 73 return u; 74 75 for(int i=20;i >= 0;--i) 76 if(fa[i][u] != fa[i][v]) 77 { 78 u=fa[i][u]; 79 v=fa[i][v]; 80 } 81 return fa[0][u]; 82 } 83 }_lca; 84 struct Set 85 { 86 int fa[maxn]; 87 void Init() 88 { 89 for(int i=0;i <= n;++i) 90 fa[i]=i; 91 } 92 int Find(int x) 93 { 94 return x == fa[x] ? x:fa[x]=Find(fa[x]); 95 } 96 void Union(int x,int y) 97 { 98 x=Find(x); 99 y=Find(y); 100 if(x != y) 101 fa[x]=y; 102 } 103 }_set; 104 void Solve() 105 { 106 for(int i=1;i <= n;++i)///将属于同一颗树的节点存在_set.fa[i]中 107 V[_set.Find(i)].push_back(i);///并查集查找i的祖先节点用Find() 108 _lca.Init(); 109 110 while(q--) 111 { 112 int u,v; 113 scanf("%d%d",&u,&v); 114 if(_set.Find(u) != _set.Find(v))///判断u,v是否属于同一棵树用Find() 115 puts("Not connected"); 116 else 117 { 118 int x=_lca.lca(u,v); 119 ll ans=_lca.dis[u]+_lca.dis[v]-2*_lca.dis[x]; 120 printf("%lld\n",ans); 121 } 122 } 123 } 124 void Init() 125 { 126 num=0; 127 for(int i=0;i <= n;++i) 128 { 129 head[i]=-1; 130 V[i].clear(); 131 } 132 _set.Init(); 133 } 134 int main() 135 { 136 // freopen("C:\\Users\\hyacinthLJP\\Desktop\\C++WorkSpace\\in&&out\\contest","r",stdin); 137 while(~scanf("%d%d%d",&n,&m,&q)) 138 { 139 Init(); 140 for(int i=1;i <= m;++i) 141 { 142 int u,v,w; 143 scanf("%d%d%d",&u,&v,&w); 144 addEdge(u,v,w); 145 addEdge(v,u,w); 146 _set.Union(u,v); 147 } 148 Solve(); 149 } 150 return 0; 151 }
•题解2
通过添加虚点将森林转化成一棵树;
并以添加的虚点作为这棵树的根节点;
对于询问操作,如果询问的两点的 $LCA$ 为虚点,那么这两点在原森林中不连通;
这么做的话,只需处理一棵树的 $dis,dep,fa$;
•Code
基于二分的LCA1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define mem(a,b) memset(a,b,sizeof(a)) 5 const int maxn=1e4+50; 6 7 int n,m,q; 8 int num; 9 int head[maxn]; 10 struct Edge 11 { 12 int to; 13 ll w; 14 int next; 15 }G[maxn<<2]; 16 void addEdge(int u,int v,ll w) 17 { 18 G[num]={v,w,head[u]}; 19 head[u]=num++; 20 } 21 /** 22 fa[i][j]:节点j沿着其父结点向上走2^i步所到的节点(超过根节点时记为-1) 23 ///dis[i]:节点i的与根节点的距离 24 ///dep[i]:节点i的深度,根节点深度为0 25 */ 26 struct LCA 27 { 28 int fa[30][maxn]; 29 ll dis[maxn]; 30 ll dep[maxn]; 31 void DFS(int u,int f,ll Dis,ll Dep) 32 { 33 fa[0][u]=f;///节点u向上走2^0步来到的节点便是其父节点 34 dis[u]=Dis; 35 dep[u]=Dep; 36 for(int i=head[u];~i;i=G[i].next) 37 { 38 int v=G[i].to; 39 ll w=G[i].w; 40 if(v != f) 41 DFS(v,u,Dis+w,Dep+1); 42 } 43 } 44 void Init() 45 { 46 DFS(n+1,-1,0,0); 47 for(int k=1;k <= 20;++k) 48 for(int u=1;u <= n+1;++u) 49 if(fa[k-1][u] == -1) 50 fa[k][u]=-1; 51 else 52 fa[k][u]=fa[k-1][fa[k-1][u]]; 53 } 54 int lca(int u,int v)///返回u,v的最近公共祖先 55 { 56 if(dep[u] > dep[v]) 57 swap(u,v); 58 59 for(int i=0;i <= 20;++i) 60 if((dep[v]-dep[u])>>i&1) 61 v=fa[i][v]; 62 if(u == v) 63 return u; 64 65 for(int i=20;i >= 0;--i) 66 if(fa[i][u] != fa[i][v]) 67 { 68 u=fa[i][u]; 69 v=fa[i][v]; 70 } 71 return fa[0][u]; 72 } 73 }_lca; 74 struct Set 75 { 76 int fa[maxn]; 77 void Init() 78 { 79 for(int i=0;i <= n+1;++i) 80 fa[i]=i; 81 } 82 int Find(int x) 83 { 84 return x == fa[x] ? x:fa[x]=Find(fa[x]); 85 } 86 void Union(int x,int y) 87 { 88 x=Find(x); 89 y=Find(y); 90 if(x != y) 91 fa[x]=y; 92 } 93 }_set; 94 void Solve() 95 { 96 _lca.Init(); 97 98 while(q--) 99 { 100 int u,v; 101 scanf("%d%d",&u,&v); 102 int x=_lca.lca(u,v); 103 if(x == n+1) 104 puts("Not connected"); 105 else 106 { 107 ll ans=_lca.dis[u]+_lca.dis[v]-2*_lca.dis[x]; 108 printf("%lld\n",ans); 109 } 110 } 111 } 112 bool vis[maxn]; 113 void Init() 114 { 115 num=0; 116 for(int i=0;i <= n+1;++i) 117 { 118 head[i]=-1; 119 vis[i]=false; 120 } 121 _set.Init(); 122 } 123 int main() 124 { 125 // freopen("C:\\Users\\hyacinthLJP\\Desktop\\C++WorkSpace\\in&&out\\contest","r",stdin); 126 while(~scanf("%d%d%d",&n,&m,&q)) 127 { 128 Init(); 129 for(int i=1;i <= m;++i) 130 { 131 int u,v,w; 132 scanf("%d%d%d",&u,&v,&w); 133 addEdge(u,v,w); 134 addEdge(v,u,w); 135 _set.Union(u,v); 136 } 137 ///定义虚节点 n+1 138 ///将节点n+1与连接每棵树的某个节点 139 ///每棵树只有一个节点与节点n+1相连,边仅加一次 140 for(int i=1;i <= n;++i) 141 if(!vis[_set.Find(i)])///此处用Find(i)而不是用fa[i] 142 { 143 addEdge(n+1,_set.Find(i),0); 144 vis[_set.Find(i)]=true; 145 } 146 147 Solve(); 148 } 149 return 0; 150 }