前言
看似很水的却有坑的题
题目
讲解
很明显,对于森林中的每棵树,我们都要先分别求出直径
而合并可以用并查集维护
关键在于如何合并
不难想到是将两棵树直径的中点连边,但是新树的直径就是两棵树的直径分别除以二(向上取整)的和再加一吗?
不是!
比如两棵树的直径分别是10,0
按上面那个方法算出来答案为6
而答案明显是10
也就是说我们需要对两棵树的直径取最大值
再讲一个自己犯的错:直径指树边而不是树点
代码
int f[MAXN];
int findSet(int x)
{
if(x != f[x]) f[x] = findSet(f[x]);
return f[x];
}
bool vis[MAXN];
int head[MAXN],tot,dp[MAXN],d;
struct edge
{
int v,nxt;
}e[MAXN << 1];
void Add_Edge(int x,int y)
{
e[++tot].v = y;
e[tot].nxt = head[x];
head[x] = tot;
}
void Add_Double_Edge(int x,int y)
{
Add_Edge(x,y);
Add_Edge(y,x);
}
void dfs(int x)
{
vis[x] = 1;
for(int i = head[x]; i ;i = e[i].nxt)
if(!vis[e[i].v])
{
f[findSet(e[i].v)] = findSet(x);
dfs(e[i].v);
d = Max(d,dp[x] + dp[e[i].v] + 1);
dp[x] = Max(dp[x],dp[e[i].v]+1);
}
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n = Read(); m = Read(); Q = Read();
while(m--) Add_Double_Edge(Read(),Read());
for(int i = 1;i <= n;++ i) f[i] = i;
for(int i = 1;i <= n;++ i)
if(!vis[i]) d = 0,dfs(i),dp[i] = d;
while(Q--)
{
int opt = Read();
if(opt == 1) Put(dp[findSet(Read())],'
');
else
{
int x = Read(),y = Read();
int u = findSet(x),v = findSet(y);
if(u == v) continue;
if(u > v) swap(u,v);
f[v] = u;
int t = Max(dp[u],dp[v]);
dp[u] = (dp[u]+1 >> 1) + (dp[v]+1 >> 1) + 1;
dp[u] = Max(dp[u],t);
}
}
return 0;
}