Codeforces 455C
题目链接:http://acm.hust.edu.cn/vjudge/problem/52671
题目大意:
给出一个初始森林,支持两种操作:
(1)输出某个点所在树的直径长度
(2)添加一条边合并两棵树,使新的树的直径长度最小
分析:
每棵树用并查集维护,同时维护每棵树直径长度,初始图两遍DFS找出直径,合并两棵树时顺便维护一下直径长度,具体看代码。
代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <vector>
#define rep(i,x,y) for (int i=x;i<=y;i++)
#define dep(i,y,x) for (int i=y;i>=x;i--)
#define sz(x) (int)(x.size())
using namespace std;
const int maxn=300000+23;
int n,m,q,k,x,y,p,maxd,F[maxn],path[maxn];
bool vis[maxn][3];
vector<int> G[maxn];
int find(int k) {return (F[k]==k)?k:F[k]=find(F[k]);}
void merge(int x,int y)
{
 x=find(x),y=find(y);
 if (x==y) return;
 F[x]=y;
 path[y]=max(path[x],max(path[y],(path[x]+1)/2+(path[y]+1)/2+1));
 path[x]=0;  //拿原来两棵树直径与新树直径比较
}
void DFS(int k,int d,int type)
{
 vis[k][type]=1;
 if (d>maxd) maxd=d,p=k;
 rep(i,0,sz(G[k])-1)
  if (!vis[G[k][i]][type]) DFS(G[k][i],d+1,type);
}
void init()
{
 memset(vis,0,sizeof(vis));
 memset(path,0,sizeof(path));
 rep(i,1,n)
  if (!vis[i][0]) 
  {
   maxd=0,DFS(i,0,0);
   maxd=0,DFS(p,0,1);
   path[find(i)]=maxd; //DFS找直径
  }
}
int main()
{
 scanf("%d%d%d",&n,&m,&q);
 
 rep(i,1,n) F[i]=i;
 rep(i,1,m)
 {
  scanf("%d%d",&x,&y);
  G[x].push_back(y);
  G[y].push_back(x);
  
  merge(x,y);
 }
 
 init();
 rep(i,1,q)
 {
  scanf("%d",&k);
  if (k==1)
  {
   scanf("%d",&x);
   printf("%d\n",path[find(x)]);
  }
  else 
  {
   scanf("%d%d",&x,&y);
   merge(x,y);
  }
 }
 return 0;
}
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号