Problem Description
Andrew plays a game called “Civilization”. Dima helps him.
The game has n cities and m bidirectional roads. The cities are numbered from 1 to n. Between any pair of cities there either is a single (unique) path, or there is no path at all. A path is such a sequence of distinct cities v1, v2, …, vk, that there is a road between any contiguous cities vi and vi + 1 (1 ≤ i < k). The length of the described path equals to (k - 1). We assume that two cities lie in the same region if and only if, there is a path connecting these two cities.
During the game events of two types take place:
Andrew asks Dima about the length of the longest path in the region where city x lies.
Andrew asks Dima to merge the region where city x lies with the region where city y lies. If the cities lie in the same region, then no merging is needed. Otherwise, you need to merge the regions as follows: choose a city from the first region, a city from the second region and connect them by a road so as to minimize the length of the longest path in the resulting region. If there are multiple ways to do so, you are allowed to choose any of them.
Dima finds it hard to execute Andrew’s queries, so he asks you to help him. Help Dima.
Input
The first line contains three integers n, m, q (1 ≤ n ≤ 3·105; 0 ≤ m < n; 1 ≤ q ≤ 3·105) — the number of cities, the number of the roads we already have and the number of queries, correspondingly.
Each of the following m lines contains two integers, ai and bi (ai ≠ bi; 1 ≤ ai, bi ≤ n). These numbers represent the road between cities ai and bi. There can be at most one road between two cities.
Each of the following q lines contains one of the two events in the following format:
1 xi. It is the request Andrew gives to Dima to find the length of the maximum path in the region that contains city xi (1 ≤ xi ≤ n).
2 xi yi. It is the request Andrew gives to Dima to merge the region that contains city xi and the region that contains city yi (1 ≤ xi, yi ≤ n). Note, that xi can be equal to yi.
Output
For each event of the first type print the answer on a separate line.
Example
Input
6 0 6
2 1 2
2 3 4
2 5 6
2 3 2
2 5 3
1 1
Output
4
大致题意:有多组数据,首先输入n,m,q。表示现在有n个城市编号1~n,有m条双向道路。接下来q行有q次操作,操作有两种,输入为1:输入一个x,输出x城市所在的连通区域块的最长路的长度,输入为2:输入x,y,如果x,y在同一连通块,则不操作;否则从两个连通块中分别选择一个城市连接,使得新的连通块的最长路长度最短,如果有多种选择则任意选一种。
思路:我们可以先根据初始边用求树的直径的方法,求出每一块连通区域块的最长路长度(即两次dfs,第一遍dfs找出距离起点最远的点x,这个点x就是最长路中的一个端点,然后以这个x点为起点再用一遍dfs得到最长路长度length,并将这块连通区域的父节点都标记为x,用len[]数组记录父节点为x的最长路的长度,len[x]=length,)
然后操作1就很容易实现,结果即为len[find(x)]。
操作2:(借用网上一个大神的推出的公式)假设fx,fy分别为两个连通区域块的父节点,令len[fx]>len[fy](如果len[fx] < len[fy]就交换fx,fy的值),我们将fy的父节点设为fx,然后更新len[fx]的值, len[fx]=max(len[fx],(len[fx]+1)/2 + (len[fy]+1)/2 + 1 )(其实我对这神奇的公式还是有点懵),然后就完事了。
代码如下
/*
链式前向星存储边
*/
#include<iostream>
#include<algorithm>
#include<string>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<cstring>
using namespace std;
const int maxn=300011;
int pre[maxn],head[maxn];
int len[maxn];//len[i]记录父节点为i的连通区域的最长路长度
int n,m,q;
int length,num;
int thefather;//连通区域的父节点
int tol;
struct Edge
{
int to;//edge[i].to表示编号为i的边所连接下个点
int next;//edge[i].next表示与编号为i的边同起点的上一条边的编号
}edge[2*maxn];
void addedge(int u,int v)
{
edge[tol].to=v;
edge[tol].next=head[u];
head[u]=tol++;
}
int find(int x)
{
int r=x;
while (pre[r]!=r)
r=pre[r];
int i=x; int j;
while(i!=r)
{
j=pre[i];
pre[i]=r;
i=j;
}
return r;
}
void dfs(int x,int prex,int step)//x:起点,prex:与x点相连的上一个点 ,step:从起点走到x点的路程
{
pre[x]=thefather;//将该连通区域的父节点都标记为thefather
if(step>length)
{
length=step;
num=x;
}
for(int i=head[x];i!=-1;i=edge[i].next)
{
if(edge[i].to!=prex) // 防止出现 prex->x,x->prex而陷入的死循环
dfs(edge[i].to,x,step+1);
}
}
void solve(int x,int y)
{
int fx=find(x);
int fy=find(y);
if(fy==fx) return;//如果在同一连通块则不操作
if(len[fx]<len[fy]) swap(fx,fy);
len[fx]=max(len[fx],(len[fx]+1)/2 + (len[fy]+1)/2 + 1 );
pre[fy]=fx;//将fy的父节点设为fx
}
int main()
{
while(scanf("%d%d%d",&n,&m,&q)!=EOF)
{
memset(head,-1,sizeof(head));
tol=0;
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
addedge(x,y);//注意双向道路
addedge(y,x);
}
for(int i=1;i<=n;i++)
pre[i]=i;
for(int i=1;i<=n;i++)
{
if(pre[i]==i)//选择一块新的连通区域的点i
{
length=-1;//初始化连通区域的最长路
thefather=i;
dfs(i,-1,0);//将i作为起点进行一次dfs找到最长路的一端num
length=-1;
thefather=num;//将找到的最长路的一端num作为起点再进行一次dfs,并将这个点作为这个连通区域的父节点
dfs(num,-1,0);
len[num]=length;//记录父节点为num的连通区域的最长路长度为length
}
}
for(int i=1;i<=q;i++)//q次查询
{
int z,x,y;
scanf("%d",&z);
if(z==1)
{
scanf("%d",&x);
printf("%d\n",len[find(x)]);
}
else
{
scanf("%d%d",&x,&y);
solve(x,y);
}
}
}
return 0;
}