hdu6241(给定树中向上向下限制求最小可能个数)

题: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\n",ans);
    }
    return 0;
}
View Code

 

posted @ 2020-10-21 00:17  starve_to_death  阅读(104)  评论(0编辑  收藏  举报