【题目大意】
给一棵n(<=200000)个节点的树,每个点为黑色或白色,一次操作可以使一个相同颜色的连通块变成另一种颜色,求使整棵树变成一种颜色的最少操作数
【解题思路】
先将相同颜色的连通块缩点,然后我们就得到了一棵黑白相间的树。
我们可以发现当确定一个点为根时的最少操作数为树的高度减一。
当以树的直径的中点为根时树的高度最小。
因此答案为(树的直径长度+1)/2。
用dp或者两次dfs求出树的直径。
【呆马】
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<iostream>
using namespace std;
const int N=200001;
struct st{int to,next;} e[2][N<<1];
int n,num,i,j,x,y,p,ans,mxd,b[N],c[N],f[2][N],cnt[2];
void add(int x,int y,int t){e[t][++cnt[t]].to=y,e[t][cnt[t]].next=f[t][x],f[t][x]=cnt[t];}
void dfs(int x,int fa,int t,int flag,int d)
{
for (int i=f[t][x];i;i=e[t][i].next)
if (e[t][i].to!=fa)
{
int y=e[t][i].to;
b[y]=c[y]==c[x]?b[x]:++num;
dfs(y,x,t,flag,d+1);
}
if (flag==1 && d>mxd) mxd=d,p=x;
if (flag==2) ans=max(ans,d);
}
int main()
{
scanf("%d\n",&n);
for (i=1;i<=n;i++) scanf("%d",&c[i]);
for (scanf("\n"),i=1;i<n;i++) scanf("%d%d\n",&x,&y),add(x,y,0),add(y,x,0);
b[1]=num=1;
dfs(1,0,0,0,0);
for (i=1;i<=n;i++)
for (j=f[0][i];j;j=e[0][j].next)
if (b[i]!=b[e[0][j].to]) add(b[i],b[e[0][j].to],1);
dfs(1,0,1,1,0);
dfs(p,0,1,2,0);
printf("%d",(ans+1)>>1);
}