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;
}