题意
给一棵树,初始都是白色,让你染黑色,每个点有两个限制 Ai,Bi ,即点 i 的子树黑点的个数不少于 Ai,除了点 i 的子树,黑点的个数不少于 Bi
求最少染的黑点的个数
题解
二分答案
则每个点子树的黑点的个数有一个区间范围 [Ai,mid−Bi]
在树上 dp, 进行区间合并,看是否会产生矛盾,最后看根节点的区间是否包含 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;
}