HDU 2874"Connections between cities"(LCA+并查集+选根建树)

传送门

•题意

  在一个包含 n 个节点 m 条边的森林中;

  有 q 次询问,每次询问求解两点间的最短距离;

  如果这两点不联通,输出 "Not connected";

•题解1

  树上任意两点间的最短距离就是最近公共祖先分别到这两点的距离和;

  那么这个问题就被转化成了LCA问题。

  因为有多棵树,所以,对于每棵树,都提前预处理出 $dis,dep$;

  并通过并查集判断询问的两点是否联通;

•Code

  1 #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
",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 }
基于二分的LCA

•题解2

  通过添加虚点将森林转化成一棵树;

  并以添加的虚点作为这棵树的根节点;

  对于询问操作,如果询问的两点的 $LCA$ 为虚点,那么这两点在原森林中不连通;

  这么做的话,只需处理一棵树的 $dis,dep,fa$;

•Code

  1 #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
",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 }
基于二分的LCA

原文地址:https://www.cnblogs.com/violet-acmer/p/9691362.html