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;
}
posted @ 2016-07-22 09:31  Krew  阅读(157)  评论(0)    收藏  举报