CF455C Civilization 题解

思路

  1. 求树的直径,并存在一个数组里。
  2. 用并查集来动态合并加维护区域信息(包括同一颗树里的有着相同祖先的点的合并,不同树之间的合并)。
  3. 假设 \(length\) 数组:对于每棵树的根节点 \(x\)\(length_{x}=\) 该树的直径长度

接下来对于每个询问(如果给出的两点在同一颗树内则忽略),利用并查集找出两棵树的根节点 x,y,并用并查集合并两棵树,
合并后树的直径为 \(\max{[(length_{x}+1)\div2+(length_{y}+1)\div 2+1],length_{x},length_{y}}]\)

因为要想直径最短,我们选择加边的点一定要在直径上,因为其他的点走到直径还要一段距离,从而增长了路径。那么直径就被选择的点分成了两段。因为我们要最小化较长的那一段,所以要让选择的点尽量靠近直径的中点。最后的答案就是 直径长度的一半向上取整。并且还要考虑原先两棵树本来就存在的直径,他们仨进行比较,最大的才是合并后的树的直径。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
const int inf=0x3f3f3f3f;
typedef long long ll;
struct Edge {
	int from,to;
};
vector<Edge>edges;
vector<int>G[maxn];
void add(int from,int to)
{
	edges.push_back({from,to});
	int m=edges.size();
	G[from].push_back(m-1);
}
bool vis[maxn],vis2[maxn];
int pos,cnt;
int length[maxn],fa[maxn];
void dfs(int x,int len)
{
	if(len>cnt) {
		pos=x;
		cnt=len;
	}
	vis[x]=true;
	for(int i=0; i<G[x].size(); i++) {
		Edge e=edges[G[x][i]];
		int u=e.to;
		if(!vis[u]) {
			dfs(u,len+1);
		}
	}
	vis[x]=false;
}
int cal(int x)//找树的直径
{
	cnt=-1;
	dfs(x,0);
	cnt=-1;
	dfs(pos,0);
	return cnt;
}
int find (int x)
{
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
void merge(int x,int y)
{
	int xx=find(x),yy=find(y);
	fa[xx]=yy;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	memset(vis,0,sizeof(false));
	memset(vis2,0,sizeof(false));
	int n,m,q;
	cin>>n>>m>>q;
	for(int i=1; i<=n; i++)
		fa[i]=i;
	for(int i=1; i<=m; i++) {
		int x,y;
		cin>>x>>y;
		add(x,y);
		add(y,x);
		merge(x,y);
	}
	for(int i=1; i<=n; i++) {
		if(fa[i]!=i||vis2[i])//因为有并查集的存在,所以只对树的根节点进行操作
			continue;
		vis2[i]=true;//标记
		length[i]=cal(i);
	}
	while(q--) {
		int op,x,y;
		cin>>op>>x;
		if(op==1) {
			cout<<length[find(x)]<<endl;
			continue;
		} else {
			cin>>y;
			int xx=find(x),yy=find(y);
			if(xx==yy)
				continue;
			int temp=((length[xx]+1)/2)+((length[yy]+1)/2)+1;
			temp=max(temp,max(length[xx],length[yy]));
			merge(x,y);//合并
			length[yy]=temp;//更新
		}
	}
}
posted @ 2024-04-13 22:49  BadBadBad__AK  阅读(13)  评论(0编辑  收藏  举报