CF1442E Black, White and Grey Tree 题解

题目链接

点击打开链接

题目解法

首先把相邻的同色点缩起来,现在只需要考虑相邻点颜色不同的情况

先化简情况,假设没有灰点
树上问题一个常见的考虑方面是直径
我们拎出树的直径,用类似 CF1943C 的方法构造最小操作次数
令直径长度为 \(dia\)

  1. \(dia\) 为奇数
    以直径中点为中心,从外向内一层一层剥离,因为每一层所有节点的颜色是一样的
    这样构造操作次数为 \(\lfloor \frac{dia}{2}\rfloor +1\)
    只有黑白相间的一条链的最小操作次数为 \(\lfloor \frac{dia}{2}\rfloor +1\),这说明了我们构造出的通解是下界
  2. \(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;
}

posted @ 2024-05-09 16:52  Farmer_D  阅读(22)  评论(0)    收藏  举报