CF734E:缩点 + 树的直径

CF734E

题意:

  • 一棵树上有黑白两种颜色的结点,每次可以把连通的一种颜色变成另一种颜色。求至少要多少次,才能是整棵树变为一种颜色。

题解

  • 先求连通块,把颜色相同的连通块放在一起。不用Tarjan,dfs就可以直接搞定了。然后缩点。
  • 然后就是求树的直径d,两次bfs搞定。最后答案就是(d + 1)/ 2。
  • 理由是:可以由中间往外,一层一层的改变颜色。比如缩点后颜色分布为0 1 0 1 0 1(颜色一定交叉)
  • 第一次变为0 1 1 1 0 1;第二次0 0 0 0 0 1;第三次1 1 1 1 1 1

代码

#include <bits/stdc++.h>
using namespace std;
int const N = 200000 + 10;
int const inf = 0x7f7f7f7f;
int n,mx,cnt;
int color[N],scc[N],vis[N],dis[N];
vector<int>G[N],G1[N];
void dfs(int u,int cnt){
	vis[u] = cnt;
	for(int i=0;i<G[u].size();i++){
		int v = G[u][i];
		if(vis[v] || color[u] != color[v])	continue;
		dfs(v,cnt);
	}
}
int bfs(int s){
	memset(dis,-1,sizeof(dis));
	queue<int>q;
	q.push(s);
	dis[s] = 0;
	while(!q.empty()){
		int p = q.front();	q.pop();
		for(int i=0;i<G1[p].size();i++){
			int v = G1[p][i];
			if(dis[v] == -1){
				dis[v] = dis[p] + 1;
				q.push(v);
			}
		}
	}
	mx = 0;
	int ans;
	for(int i=1;i<=cnt;i++){
		if(dis[i] >= mx){
			mx = dis[i];
			ans = i;
		}
	}
	return ans;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&color[i]);
	for(int i=1;i<=n-1;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	cnt = 0;
	for(int i=1;i<=n;i++)   //查找连通块
		if(!vis[i])	dfs(i,++cnt);
	for(int i=1;i<=n;i++){
		for(int j=0;j<G[i].size();j++){
			int v = G[i][j];
			if(vis[i] != vis[v])	G1[vis[i]].push_back(vis[v]);
		}
	}
	bfs(bfs(1));
	printf("%d\n",(mx+1)/2);
	return 0;
}

 

猜你喜欢

转载自blog.csdn.net/weixin_42264485/article/details/89060630
今日推荐