题目链接:http://codeforces.com/problemset/problem/455/C
题意:
给你一个森林,n个点,m条边。
然后有t个操作。共有两种操作:
(1)1 x:
输出节点x所在树的直径。
(2)2 x y:
如果x,y不在同一棵树上的话,用一条边连接x,y所在的树,并且使得到的新树的直径尽可能小。
题解:
首先对于初始状态,算出每一棵树的直径d[find(i)]。
每次合并树的时候,因为要尽可能让新树直径变小,所以显然这条边要分别连接两棵树直径的“中点”。
所以新树的直径 = max( d[x], d[y], ceil(d[x]/2)+ceil(d[y]/2)+1 )
然后用并查集合并就好啦。
AC Code:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <math.h> 5 #include <vector> 6 #define MAX_N 300005 7 8 using namespace std; 9 10 int n,m,t; 11 int maxd; 12 int op,ed; 13 int d[MAX_N]; 14 int par[MAX_N]; 15 vector<int> edge[MAX_N]; 16 17 void init_union_find() 18 { 19 for(int i=1;i<=n;i++) 20 { 21 par[i]=i; 22 } 23 } 24 25 int find(int x) 26 { 27 return par[x]==x ? x : par[x]=find(par[x]); 28 } 29 30 void unite(int x,int y) 31 { 32 int px=find(x); 33 int py=find(y); 34 if(px==py) return; 35 par[px]=py; 36 } 37 38 bool same(int x,int y) 39 { 40 return find(x)==find(y); 41 } 42 43 void read() 44 { 45 scanf("%d%d%d",&n,&m,&t); 46 init_union_find(); 47 int x,y; 48 for(int i=1;i<=m;i++) 49 { 50 scanf("%d%d",&x,&y); 51 edge[x].push_back(y); 52 edge[y].push_back(x); 53 unite(x,y); 54 } 55 } 56 57 void dfs(int now,int p,int nd,int &v) 58 { 59 if(nd>maxd) 60 { 61 maxd=nd; 62 v=now; 63 } 64 for(int i=0;i<edge[now].size();i++) 65 { 66 int temp=edge[now][i]; 67 if(temp!=p) dfs(temp,now,nd+1,v); 68 } 69 } 70 71 void work() 72 { 73 for(int i=1;i<=n;i++) 74 { 75 if(find(i)==i) 76 { 77 maxd=-1; 78 dfs(i,-1,0,op); 79 maxd=-1; 80 dfs(op,-1,0,ed); 81 d[i]=maxd; 82 } 83 } 84 int opt,x,y; 85 while(t--) 86 { 87 scanf("%d",&opt); 88 if(opt==1) 89 { 90 scanf("%d",&x); 91 printf("%d\n",d[find(x)]); 92 } 93 else 94 { 95 scanf("%d%d",&x,&y); 96 if(!same(x,y)) 97 { 98 d[find(y)]=max(max(d[find(x)],d[find(y)]), 99 (int)(ceil(d[(find(x))]/2.0)+ceil(d[find(y)]/2.0)+1)); 100 unite(x,y); 101 } 102 } 103 } 104 } 105 106 int main() 107 { 108 read(); 109 work(); 110 }