2017端午欢乐赛——Day1T3(树的直径+并查集)

    //前些天的和jdfz的神犇们联考的模拟赛。那天上午大概是没睡醒吧,考场上忘了写输出-1的情况,白丢了25分真是**。

题目描述

    小C所在的城市有 n 个供电站,m条电线。相连的供电站会形成一个供电群,那么为了节省材料,供电群是一棵树的形式,也即城市是一个森林的形式(树V个点,V-1条边的无向连通图,森林:若干棵树)。每个供电群中不需要所有供电站都工作,最少只需要一个工作,其余的就都会通过电线收到电,从而完成自己的供电任务。当然,这样会产生延迟。定义两个供电站的延迟为它们之间的电线数量,供电群的最大延迟为其中两两供电站的延迟中最大的那一个,即树的直径。小C现有q个操作,每个操作形如“a b”,表示想要将a所在的供电群中任意一个供电站和b所在的供电群中任意一个供电站之间连一条电线,使得它们成为同一个供电群,并且使这个供电群的最大延迟最小。请输出这个最小的最大延迟。如果 a和 b已经在同一个供电群中,则输出-1,此时不需要进行连线操作。

————————————————————————————————————————————————

输入

    第一行两个整数 n,m (m<n)
    下面 m 行,每行两个整数 a,b,代表 a,b 间有一条无向边
  下面一行一个整数 q
  下面 q 行,每行两个整数 a,b,代表操作

————————————————————————————————————————————————

输出

  输出 q 行,第 i 行一个整数代表操作 i 的答案。

—————————————————————————————————————————————

样例输入

  10 7

  1 2

  1 3

  3 4

  3 5

  6 7

  8 9

  8 10

  2

  1 6

  2 8

————————————————————————————————————————————————

样例输出

  4

  4

————————————————————————————————————————————————

样例解释与数据范围

 

 

————————————————————————————————————————————————————

    显然树的直径+并查集

    而连接两棵树,若使其连完以后直径最小,则必须连两棵树的直径的中点。

    合并后的直径 max(gg[v],max(gg[u],(gg[v]+1)/2+(gg[u]+1)/2+1));

    可以稍微预处理出树的直径。

    记得判断a和b在同一个供电群的情况。

—————————————————————————————————————————————————

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstdlib>
  4 #include<algorithm>
  5 #include<cstring>
  6 const int maxn=1000009; 
  7 using namespace std;
  8 
  9 int n,m,QAQ;//n个供电站,m条电线,Q个操作 
 10 int head[maxn],nxt[maxn<<1],edge[maxn<<1];
 11 int k,flag;
 12 int q[maxn],visited[maxn];
 13 int fa[maxn];
 14 int gg[maxn]; 
 15 
 16 void add(int u,int v)
 17 {
 18     edge[k]=v;
 19     nxt[k]=head[u];
 20     head[u]=k++;
 21 }
 22 
 23 void bfs(int u,int fl)
 24 {
 25     int i,rear=0,frt=0;
 26     q[rear++]=u;
 27     visited[u]=1;
 28     while(frt<rear)
 29     {
 30         int h=q[frt++];
 31         for(i=head[h];i!=-1;i=nxt[i])
 32         {
 33             if(!visited[edge[i]])
 34             {
 35                 visited[edge[i]]=visited[h]+1;
 36                 q[rear++]=edge[i];
 37             }
 38         }
 39     }
 40 
 41     if(!fl){for(i=rear-1;i>=0;i--) visited[q[i]]=0;}
 42     flag=q[rear-1];
 43     
 44 }
 45 
 46 int find(int x)
 47 {
 48     if(fa[x]==x)return x;
 49     else return fa[x]=find(fa[x]);
 50 }
 51 
 52 int maxx(int x,int y)
 53 {
 54     return x>y?x:y;
 55 }
 56 
 57 int main()
 58 {
 59     freopen("connect.in","r",stdin);
 60     freopen("connect.out","w",stdout);
 61     int u,v;
 62     while(scanf("%d%d",&n,&m)!=EOF)
 63     {
 64         memset(head,-1,sizeof(head));
 65         memset(gg,0,sizeof(gg));
 66         memset(visited,0,sizeof(visited));
 67         k=0;
 68 
 69         for(int i=1;i<=n;i++)
 70             fa[i]=i;
 71 
 72         for(int i=0;i<m;i++)
 73         {
 74             scanf("%d%d",&u,&v);
 75             add(u,v);
 76             add(v,u);
 77             u=find(u);
 78             v=find(v);
 79             if(u!=v) fa[u]=v;
 80         }
 81 
 82         for(int i=1;i<=n;i++)
 83             fa[i]=find(i);
 84         for(int i=1;i<=n;i++)
 85         {
 86             if(!visited[i])
 87             {
 88                 bfs(i,0);
 89                 bfs(flag,1);
 90                 gg[fa[i]]=visited[flag]-1;
 91             }
 92         }
 93         cin>>QAQ; 
 94         for(int i=0;i<QAQ;i++)
 95         {
 96             scanf("%d%d",&u,&v);
 97             u=find(u);
 98             v=find(v);
 99             if(u!=v)
100             { 
101                 fa[u]=v;
102                 int mm=maxx(gg[u],gg[v]);
103                 gg[v]=maxx((gg[v]+1)/2+(gg[u]+1)/2+1,mm);//除以2向上取整 
104                 gg[u]=0;
105             } 
106             if(gg[u]==gg[v]) //同一供电群 
107             {
108                 cout<<"-1"<<endl;
109                 continue;
110             }
111             printf("%d
",gg[find(u)]);
112         }
113     }
114     fclose(stdin);
115     fclose(stdout);
116     return 0;
117 }
View Code
原文地址:https://www.cnblogs.com/Beckinsale/p/6932051.html