题意:给一棵黑白染色过的树,每次操作可以将一块连通的同色子图(连通块内不含另一颜色)染成另一颜色,问最小需要操作几次才能将树上所有节点统一为同一颜色。
题解:先用并查集缩点,把同一颜色的连通的缩成一个点,得到新树,然后直径上的点数/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;
}