[CF 734E] Anton and Tree

7 篇文章 0 订阅

题目

洛谷

题意

有一棵树,每个点初始有黑白色,每次操作将一个同色连通块颜色翻转——至少多少次翻转能让整张图颜色相同。

思路

首先,可以想到:一开始可以将同色的连通块缩成一个点——这点很显然,就不说明了。

缩点后,得到一张黑白分层图。现在是在这张黑白分层图上得到答案。怎么做呢?给出一个结论——答案为缩点后图的直径加一除以二。即记直径为 d d d A n s = ( d + 1 ) / 2 Ans=(d+1)/2 Ans=(d+1)/2

证明

现在考虑如何证明这个结论:懒得搞图,就不放图了。——刚刚本来还准备证一番, P P L \tt PPL PPL 突然告诉我:拿到直径,一直翻转直径中间这个点不就OK了?翻转次数为 ( d + 1 ) / 2 (d+1)/2 (d+1)/2也就不难理解了

Update

w y x \tt wyx wyx 的提醒才发现,并查集根本不需要,直接判颜色就行了。

Updated Code
vector<LL> G[MAXN];
bool Color[MAXN];
LL Fir[MAXN], Sec[MAXN], D[MAXN], Ans;
void GetZhi(LL x,LL Fa)
{
	int l = G[x].size();
	for (Int i = 0; i < l; ++ i)
	{
		LL to = G[x][i];
		if (to == Fa)
			continue;
		GetZhi(to, x);
		Ans = Max(Ans, D[x] + D[to] + (Color[x] != Color[to]));
		D[x] = Max(D[x], D[to] + (Color[x] != Color[to]));
	}
}
int main()
{
	LL n;
	read( n );
	for (Int i = 1; i <= n; ++ i)
		scanf("%d", &Color[i]);
	for (Int i = 1; i < n; ++ i)
	{
		LL u, v;
		read( u ); read( v );
		G[u].push_back( v );
		G[v].push_back( u ); 
	}
	GetZhi(1, 1);
	printf("%lld", (Ans + 1) / 2);
	return 0;
}

Code

LL Fa[MAXN];
vector<LL> G[MAXN];
inline void MakeSet(LL x)
{
	for (Int i = 1; i <= x; ++ i)
		Fa[i] = i;
}
LL FindSet(LL x)
{
	if (Fa[x] != x)
		Fa[x] = FindSet( Fa[x] );
	return Fa[x];
}
inline void UnionSet(LL a,LL b)
{
	LL u = FindSet( a );
	LL v = FindSet( b );
	if (u != v)
		Fa[u] = v;
}
bool Color[MAXN];
void Merge(LL x,LL Fa)
{
	int l = G[x].size();
	for (Int i = 0; i < l; ++ i)
	{
		LL to = G[x][i];
		if (to == Fa)
			continue;
		if (Color[to] == Color[x])
			UnionSet(to, x);
		Merge(to, x);
	}
}
LL Fir[MAXN], Sec[MAXN], D[MAXN], Ans;
void GetZhi(LL x,LL Fa)
{
	int l = G[x].size();
	for (Int i = 0; i < l; ++ i)
	{
		LL to = G[x][i];
		if (to == Fa)
			continue;
		GetZhi(to, x);
		Ans = Max(Ans, D[x] + D[to] + (FindSet( x ) != FindSet( to )));
		D[x] = Max(D[x], D[to] + (FindSet( x ) != FindSet( to )));
	}
}
int main()
{
	LL n;
	read( n );
	MakeSet( n );
	for (Int i = 1; i <= n; ++ i)
		scanf("%d", &Color[i]);
	for (Int i = 1; i < n; ++ i)
	{
		LL u, v;
		read( u ); read( v );
		G[u].push_back( v );
		G[v].push_back( u ); 
	}
	Merge(1, 1);
	GetZhi(1, 1);
	printf("%lld", (Ans + 1) / 2);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值