题:http://acm.hdu.edu.cn/showproblem.php?pid=6241
题意:给定n个点的树,给定A约束和B约束,[x,y]分别表示以x为子树的节点至少要有y个黑点,除x子树外的节点至少有y个节点,问满足所有约束的最少黑点数是多少,不存在则输出-1
分析:二分答案,关键是把B约束转化为x子树,那就让二分答案转化B约束;
具体地,让B约束变为x子树最多能有多少个黑点,那么A就是下界,B就是上界;
check就用dfs不断地最小代价地减少上界,若最终上界仍然大于下界,且mid在俩者(根节点的上下界)之间,则return true。
#include<bits/stdc++.h> using namespace std; #define pb push_back const int M=1e5+5; int sz[M],A[M],B[M],fir[M],sec[M],n,m1,m2; vector<int>g[M]; void dfs1(int u,int fa){ sz[u]=1; int cnt=0; for(auto v:g[u]){ if(v!=fa){ dfs1(v,u); sz[u]+=sz[v]; cnt+=A[v]; } } A[u]=max(A[u],cnt); } bool dfs2(int u,int fa){ int cnt=0; for(auto v:g[u]){ if(v!=fa){ if(!dfs2(v,u)) return false; cnt+=B[v]; } } B[u]=min(B[u],cnt+1); if(A[u]>B[u])return false; return true; } bool check(int mid){ for(int i=1;i<=n;i++) B[i]=sz[i]; for(int i=1;i<=m2;i++) B[fir[i]]=min(B[fir[i]],mid-sec[i]); if(dfs2(1,0)&&A[1]<=mid&&mid<=B[1]) return true; return false; } int main(){ int T; scanf("%d",&T); while(T--){ scanf("%d",&n); for(int i=1;i<=n;i++) g[i].clear(),A[i]=0,B[i]=0; for(int u,v,i=1;i<n;i++){ scanf("%d%d",&u,&v); g[u].pb(v); g[v].pb(u); } scanf("%d",&m1); while(m1--){ int x,y; scanf("%d%d",&x,&y); A[x]=max(A[x],y); } dfs1(1,0); scanf("%d",&m2); for(int i=1;i<=m2;i++) scanf("%d%d",&fir[i],&sec[i]); int l=1,r=n,ans=-1; while(l<=r){ int midd=(l+r)>>1; if(check(midd)){ ans=midd; r=midd-1; } else l=midd+1; } printf("%d ",ans); } return 0; }