Codeforces 734E Anton and Tree(并查集+树的直径)

传送门

题意:给一棵黑白染色过的树,每次操作可以将一块连通的同色子图(连通块内不含另一颜色)染成另一颜色,问最小需要操作几次才能将树上所有节点统一为同一颜色。

题解:先用并查集缩点,把同一颜色的连通的缩成一个点,得到新树,然后直径上的点数/2即可。(从直径中间那个点开始染色,把同色的区间不断往直径两端扩展,这个过程中非直径上的点的颜色也会被统一)

为什么这个染法操作数最小,因为这是将直径上的点统一颜色所需操作数的下界。

脑洞还是不够大....树的直径又没看出来诶...

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int N=2e5+4;
int n,fa[N];
int c[N];
int dep[N];
vector<int> G[N];
struct Edge {
	int u,v;
}e[N];
int etot;
inline int read() {
	int x=0,f=1;char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
	return x*f;
}
inline int find(int x) {
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
inline void merge(int x,int y) {
	int fx=find(x),fy=find(y);
	if (fx^fy) fa[fx]=fy;
}
inline void smax(int &x,int y) {
	x=x<y?y:x;
}
inline void dfs(int p,int fa) {
	dep[p]=dep[fa]+1;
	for (int i=0;i<G[p].size();++i) {
		int v=G[p][i];
		if (v^fa) dfs(v,p);
	}
}
int main() {
	n=read();
	for (register int i=1;i<=n;++i) {
		c[i]=read();
		fa[i]=i;
	}
	for (register int i=1;i<n;++i) {
		int u=read(),v=read();
		if (c[u]==c[v]) merge(u,v);
		else {
			++etot;
			e[etot].u=u;
			e[etot].v=v;
		}
	}
	for (register int i=1;i<=n;++i) find(i);
	int st=-1;
	for (register int i=1;i<=etot;++i) {
		int u=e[i].u,v=e[i].v;
		st=fa[u];
		G[fa[u]].push_back(fa[v]);
		G[fa[v]].push_back(fa[u]);
	}
	if (st==-1) {
		puts("0");
		return 0;
	}
	dfs(st,0);
	int mxdep=0;
	for (register int i=1;i<=n;++i)
		if (mxdep<dep[i]) {
			mxdep=dep[i];
			st=i;
		}
	dfs(st,0);
	mxdep=0;
	for (register int i=1;i<=n;++i)
		smax(mxdep,dep[i]);
	printf("%d\n",mxdep>>1);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值