题解:这一题给人的第一感觉是要用并查集,而且我需要知道并查集的直径(最长的路径)。现在假设有两个点X,Y且他们不属于一个集合,可以想到我如果知道了X和Y各自所在集合的直径,我又不用去关心如何合并,我一定可以求出X和Y这两个集合合并后集合的最大直径且使他最小。方程为 MAX(MAX(WAY[FATHER(X)],WAY[FATHER(Y)]),WAY[(FATHER(X)]+1)/2+WAY[FATHER(Y)]+1)/2+1 )
后面那个之所以是+1在整除2是因为要上取整,前面之所以求X和Y各自所在集合的最大值是因为集合本身可能有比合并后理想长度更大的路径。这样我们求出了方程,我们把每个集合的解记录在根上就可以了。
//求直径的时候参考了别人的方法- = 这是他的代码: http://xwk.iteye.com/blog/2130688
并查集要优化,不然超时.....
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
vector<int> c[300002];
int a,b,n,m,q,t,i,root,ans,x,y,way[300002],f[300002];
int father(int k)
{
if (f[k]==k) return f[k];
else return f[k]=father(f[k]);
}
void slove(int x,int y)
{
int p,v,t,g,h;
t=father(x); v=father(y);
if (t==v) return;
p=max(way[t],way[v]);
g=way[t]; h=way[v];
f[t]=v;
way[v]=max(p,(++g)/2+(++h)/2+1);
}
void dfs(int k,int l,int d)
{
f[k]=root;
if (d>ans)
{
ans=d;
t=k;
}
vector<int>::iterator it;
for (it=c[k].begin();it!=c[k].end();it++)
if (*it!=l) dfs(*it,k,d+1);
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for (int i=0;i<=n;i++) c[i].clear();
for (int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
c[a].push_back(b);
c[b].push_back(a);
}
// set
for (int i=1;i<=n;i++) f[i]=i;
for (int i=1;i<=n;i++)
if (f[i]==i)
{
root=i;
ans=-1;
dfs(i,0,0);
ans=-1;
dfs(t,0,0);
way[i]=ans;
}
for (int i=1;i<=q;i++)
{
scanf("%d",&a);
if (a==1)
{
scanf("%d",&x);
cout <<way[father(x)]<<endl;
}
else
{
scanf("%d%d",&x,&y);
slove(x,y);
}
}
return 0;
}