题意:
给定n个城市和m个道路,在同一个连通块的城市为一个区域。
有两个操作,第一个操作:询问第x个城市所在的区域最长链是多少。第二个操作:将第x个城市所在的区域和第y个城市所在的区域相连接,要求连接后的最长链最短。
思路:
先来考虑第一个操作,求连通域内最长链可用两次dfs,然后我们记录下最长链中间的结点,令len[u][0]和len[u][1]分别为最长链左边的长度和右边的长度,然后将其余结点的根节点设改该结点,用并查集,则每次查询只用找到询问结点的根结点即可。
第二个操作则,若要求连接后的最长链最短,则一定是将两个区域的根结点相连,相连之后令一个区域根节点为另一个区域根节点,更新len[u][0]和len[u][1],具体更新看代码。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define mem(name,value) memset(name,value,sizeof(name))
#define FOR(i,n) for(int i=1;i<=n;i++)
using namespace std;
const int maxn = 3*100000+10;
int path[maxn],len[maxn][2],p[maxn];
int path_max,path_start,path_mid;
bool vis[maxn];
vector<int>G[maxn];
void dfs1(int s,int cur,int f){ //第一次dfs找到最长链的起点
if(cur>=path_max){
path_max = cur;
path_start = s;
}
for(int i=0;i<G[s].size();i++)
if(G[s][i]!=f) dfs1(G[s][i],cur+1,s);
}
void dfs2(int s,int cur,int f){//第二次dfs算出最长链和找到最长链的中间结点
path[cur]=s;
if(cur>=path_max){
path_max = cur;
path_mid = path[cur/2];
}
for(int i=0;i<G[s].size();i++)
if(G[s][i]!=f) dfs2(G[s][i],cur+1,s);
}
void dfs3(int s,int f){ //第三次dfs将其余点都设为该结点的子节点,并标记所有访问过的结点
vis[s]=true;
p[s] = path_mid;
for(int i=0;i<G[s].size();i++)
if(G[s][i]!=f) dfs3(G[s][i],s);
}
int find_set(int x){return p[x]==x ? x : p[x]=find_set(p[x]);}
void merge_set(int a,int b){
int x = find_set(a), y = find_set(b);
if(x==y) return ;
int x1 = max(len[x][0],len[x][1]);
int y1 = max(len[y][0],len[y][1]);
if(x1<y1) swap(x,y);
p[y] = x;
x1 = max(len[x][0],len[x][1]);
y1 = max( min(len[x][0],len[x][1]),max(len[y][0],len[y][1])+1 );
len[x][0] = x1; len[x][1] = y1;
}
int main(){
//freopen("in.txt","r",stdin);
int n,m,q,tmp,x,y;
scanf("%d%d%d",&n,&m,&q);
for(int i=0;i<m;i++){
scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
mem(vis,false); mem(len,0);
for(int i=1;i<=n;i++){
if(!vis[i]){
path_max = 0;
dfs1(i,0,-1);
dfs2(path_start,0,-1);
dfs3(path_mid,-1);
len[path_mid][0]=path_max / 2;
len[path_mid][1] = path_max - len[path_mid][0];
}
}
for(int i=0;i<q;i++){
scanf("%d",&tmp);
if(tmp==1){
scanf("%d",&x);
tmp = find_set(x);
printf("%d\n",len[tmp][0]+len[tmp][1]);
}else{
scanf("%d%d",&x,&y);
merge_set(x,y);
}
}
return 0;
}