题意

给一棵树,初始都是白色,让你染黑色,每个点有两个限制 A i , B i A_i,B_i Ai,Bi ,即点 i i i 的子树黑点的个数不少于 A i A_i Ai,除了点 i i i 的子树,黑点的个数不少于 B i B_i Bi
求最少染的黑点的个数

题解

二分答案
则每个点子树的黑点的个数有一个区间范围 [ A i , m i d B i ] [A_i,mid-B_i] [Ai,midBi]
在树上 d p dp dp, 进行区间合并,看是否会产生矛盾,最后看根节点的区间是否包含 m i d mid mid 即可

代码

#include<bits/stdc++.h>
#define N 100010
#define INF 0x3f3f3f3f
#define eps 1e-5
#define pi 3.141592653589793
#define mod 1000000007
#define P 1000000007
#define LL long long
#define pb push_back
#define fi first
#define se second
#define cl clear
#define si size
#define lb lower_bound
#define ub upper_bound
#define bug(x) cerr<<#x<<" : "<<x<<endl
#define mem(x,y) memset(x,0,sizeof(int)*(y+3))
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;

int A,B,n;
vector<int> G[N];
int d[N],u[N],a[N],b[N];
int sz[N],fg,ans;

void dfs(int x,int fa){
    sz[x]=1;
    for(auto i:G[x])if (i!=fa){
        dfs(i,x);
        sz[x]+=sz[i];
    }
}

bool ok(int x,int fa){
    int d=0,u=1;
    for(auto i:G[x])if(i!=fa){
        if(!ok(i,x)) return 0;
        d+=a[i];
        u+=b[i];
    }
    a[x]=max(a[x],d);
    b[x]=min(b[x],u);
    return a[x]<=b[x];
}

int main()
{
    int T; sc(T);
    while(T--){
        sc(n);
        for(int i=1;i<=n;i++) G[i].cl(),d[i]=u[i]=0;
        for(int i=1,x,y;i<n;i++){
            scc(x,y);
            G[x].pb(y); G[y].pb(x);
        }
        sc(A); for(int i=1,x,y;i<=A;i++) scc(x,y), d[x]=max(d[x],y);
        
        sc(B); for(int i=1,x,y;i<=B;i++) scc(x,y), u[x]=max(u[x],y);
        
        fg=0;
        dfs(1,-1);
        for(int i=1;i<=n;i++) if (sz[i]<d[i]||n-sz[i]<u[i]) fg=1;
        if (fg ) {puts("-1");continue; }

        int l=*max_element(d+1,d+n+1),r=n,ans;
        while(l<=r){
            int t=l+r>>1;
            for(int i=1;i<=n;i++) a[i]=d[i],b[i]=t-u[i];
            if (ok(1,-1)&&a[1]<=t&&t<=b[1]) ans=t,r=t-1;
            else l=t+1;
        }
        printf("%d\n",ans);
    }
    return 0;
}