• 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

  • 相关阅读:
    逐步实现python版wc命令
    Linux中短横线(-)小记
    memcached启动脚本(class练习)
    nginx启动脚本(class练习)
    Python-类的方法
    re模块
    shutil模块
    时间模块(time/date)
    迭代器
    生成器
  • 原文地址:https://www.cnblogs.com/violet-acmer/p/9691362.html
Copyright © 2020-2023  润新知