• Codeforces Round #523 (Div. 2) E. Politics(最小费+思维建图)


    https://codeforces.com/contest/1061/problem/E

    题意

    有n个点(<=500),标记第i个点的代价a[i],然后分别在这n个点建两棵树,对于每颗树的每个点来说,都有一个p[i],代表他的子树中被标记的点的个数需要等于p[i],请你选择需要标记的点,使得可以满足上述两棵树的要求,并且使得代价最大,输出最大的代价或者-1(不存在)

    思路

    • 这道题难在建图
    • 首先考虑子树问题,是否选择每个节点对他的子孙节点没有影响,但是对他自身及其祖先节点有影响,注意p[i]=0代表i的子树没有限制

      假设v为u的子孙节点且p[u]>0(有限制),sum=p[v1]+p[v2]+..+p[vn],
      sum>p[u],则对于本棵树来说不存在满足条件的选点情况
      sum<=p[u],则剩下p[u]-sum的空间给本棵树和另外一颗树选择

    • 设col[u]为最靠近u的有限制的祖先节点(包括u),那么选择u就等于填充了两棵树的col[u]节点
    • 所以可以这样建图,建超源超汇S,T,将第一颗树的点和源点相连,cap=p[u]-sum,cost=0,将第二颗树的点和汇点相连,cap=p[u]-sum,cost=0,将两棵树之间的点col1[u]和col2[u]相连,cap=1,cost=-a[u],跑一个mcmf
    • 还需要判一下左边容量等于右边容量并且最大流等于容量,才能存在满足条件的选点情况
    #include<bits/stdc++.h>
    #define N 2005
    #define M 4000000
    #define inf 0x3f3f3f3f
    using namespace std;
    int n,rt1,rt2,ent,S,T,i,u,v,q,x,hd[N],p[N],col[N],f[N],d[N],inq[N],pre[N],a[N];
    int flow,cost,ans,in,out;
    
    struct edge{
        int u,v,nt,c,w;
        edge(int u=0,int v=0,int nt=0,int c=0,int w=0):u(u),v(v),nt(nt),c(c),w(w){}
    }E[M];
    
    void fail(){
        cout<<-1;exit(0);
    }
    void add(int u,int v,int c,int w){
        E[++ent]=edge(u,v,hd[u],c,w);hd[u]=ent;
        E[++ent]=edge(v,u,hd[v],0,-w);hd[v]=ent;
    }
    int dfs(int u,int fa,int tar){
        if(p[u])tar=u;
        col[u]=tar;
        int cnt=0,tp=p[u];
        for(int i=hd[u];i;i=E[i].nt){
            int v=E[i].v;
            if(v==fa)continue;
            cnt+=dfs(v,u,tar);
        }
        if(p[u]){if(p[u]<cnt)fail();p[u]-=cnt;cnt=tp;}
        return cnt;
    }
    
    int bf(){
        queue<int>Q;
        for(i=0;i<=T;i++)d[i]=inf;
        memset(inq,0,sizeof(inq));
        d[S]=0;inq[S]=1;pre[S]=0;f[S]=inf;Q.push(S);
    
        while(!Q.empty()){
            int u=Q.front();Q.pop();
            inq[u]=0;
            for(int i=hd[u];i;i=E[i].nt){
                edge &e=E[i];
                if(e.c&&d[e.v]>d[u]+e.w){
                    d[e.v]=d[u]+e.w;
                    pre[e.v]=i;
                    f[e.v]=min(f[u],e.c);
                    if(!inq[e.v]){inq[e.v]=1;Q.push(e.v);}
                }
            }
        }
        return d[T]<inf;
    }
    
    void mcmf(){
        if(in!=out)fail();
        flow=0;cost=0;
        while(bf()){
            flow+=f[T];cost-=d[T]*f[T];
            for(int i=T;i!=S;i=E[pre[i]].u){
                E[pre[i]].c-=f[T];
                E[pre[i]^1].c+=f[T];
            }
        }
        if(flow!=in)fail();
        cout<<cost<<endl;
    }
    
    int main(){
        cin>>n>>rt1>>rt2;rt2+=n;
        ent=1;S=2*n+1;T=2*n+2;
        for(i=1;i<=n;i++)scanf("%d",&a[i]);
        for(i=0;i<n-1;i++){scanf("%d%d",&u,&v);add(u,v,0,0);}
        for(i=0;i<n-1;i++){scanf("%d%d",&u,&v);u+=n;v+=n;add(u,v,0,0);}
        cin>>q;
        for(i=0;i<q;i++){scanf("%d%d",&u,&x);p[u]=x;}
        cin>>q;
        for(i=0;i<q;i++){scanf("%d%d",&u,&x);p[u+n]=x;}
        dfs(rt1,0,rt1);dfs(rt2,0,rt2);
    
        memset(hd,0,sizeof(hd));ent=1;
        in=out=0;
        for(i=1;i<=n;i++){in+=p[i];if(p[i])add(S,i,p[i],0);}
        for(i=1;i<=n;i++){out+=p[i+n];if(p[i+n])add(i+n,T,p[i+n],0);}
        for(i=1;i<=n;i++)add(col[i],col[i+n],1,-a[i]);
        mcmf();
    }
    
  • 相关阅读:
    win8及win8.1商店出现0X80073CF9的解决办法!
    Ubuntu 14.04 登陆界面循环问题解决
    Java学习笔记-Json
    Java学习笔记-Thread-线程
    git学习笔记
    Java学习笔记-File
    java学习笔记-set
    C# 实验4 数据库
    C#文件处理
    C#-实验3
  • 原文地址:https://www.cnblogs.com/VIrtu0s0/p/10043446.html
Copyright © 2020-2023  润新知