[CF455C] Civilization

前言

看似很水的却有坑的题

题目

洛谷

CF

讲解

很明显,对于森林中的每棵树,我们都要先分别求出直径

而合并可以用并查集维护

关键在于如何合并

不难想到是将两棵树直径的中点连边,但是新树的直径就是两棵树的直径分别除以二(向上取整)的和再加一吗?

不是!

比如两棵树的直径分别是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())],'\n');
		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;
}
posted @ 2020-08-17 10:09  皮皮刘  阅读(84)  评论(0编辑  收藏  举报