CF1442E Black, White and Grey Tree 题解
题目链接
题目解法
首先把相邻的同色点缩起来,现在只需要考虑相邻点颜色不同的情况
先化简情况,假设没有灰点
树上问题一个常见的考虑方面是直径
我们拎出树的直径,用类似 CF1943C 的方法构造最小操作次数
令直径长度为 \(dia\)
- \(dia\) 为奇数
以直径中点为中心,从外向内一层一层剥离,因为每一层所有节点的颜色是一样的
这样构造操作次数为 \(\lfloor \frac{dia}{2}\rfloor +1\)
只有黑白相间的一条链的最小操作次数为 \(\lfloor \frac{dia}{2}\rfloor +1\),这说明了我们构造出的通解是下界 - \(dia\) 为偶数
以直径中间两点中的任意一点开始做上面的操作,操作次数为 \(\frac{dia}{2} +1\)
同样的,由一条链的情况可以得出这是下界
所以最小操作次数为 \(\lfloor \frac{dia}{2}\rfloor +1\)
有灰点的情况就比较复杂了
我们不从构造的角度看,而从把灰点染成黑点或白点,然后做上面的构造,就豁然开朗了
考虑二分答案,然后树形 \(dp\),只要令 \(f_{i,0/1}\) 表示把点 \(i\) 染成黑/白,子树中最长能延伸的长度,后面的 \(dp\) 就显然了
时间复杂度 \(O(n\log n)\)
#include <bits/stdc++.h>
#define F(i,x,y) for(int i=(x);i<=(y);i++)
#define DF(i,x,y) for(int i=(x);i>=(y);i--)
#define ms(x,y) memset(x,y,sizeof(x))
#define SZ(x) (int)x.size()-1
#define all(x) x.begin(),x.end()
#define pb push_back
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
typedef pair<int,int> pii;
template<typename T> void chkmax(T &x,T y){ x=max(x,y);}
template<typename T> void chkmin(T &x,T y){ x=min(x,y);}
template<typename T> void read(T &FF){
FF=0;int RR=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
FF*=RR;
}
const int N=200010,inf=1e9;
vector<int> G[N];
int n,a[N],dia,f[N][2];bool fl;
void dfs(int u,int fa){
for(int v:G[u]) if(v!=fa) dfs(v,u);
bool ok=0;
F(i,0,1) if(a[u]!=(i^1)){
int mx1=0,mx2=0;
for(int v:G[u]) if(v!=fa){
int g=min(f[v][i]-1,f[v][i^1]);
if(g>mx1) mx2=mx1,mx1=g;else chkmax(mx2,g);
}
if(mx1+mx2+1>dia) f[u][i]=inf;
else f[u][i]=mx1+1,ok=1;
}
if(!ok) fl=0;
}
bool check(){ F(i,1,n) f[i][0]=f[i][1]=inf;fl=1,dfs(1,0);return fl;}
void work(){
read(n);
F(i,1,n) read(a[i]),a[i]=(a[i]+2)%3;
F(i,1,n-1){
int x,y;read(x),read(y);
G[x].pb(y),G[y].pb(x);
}
int lo=0,hi=n+1;
while(lo<hi-1){
dia=(lo+hi)>>1;
check()?hi=dia:lo=dia;
}
printf("%d\n",hi/2+1);
F(i,1,n) G[i].clear();
}
int main(){
int T;read(T);
while(T--) work();
return 0;
}

浙公网安备 33010602011771号