dtoj#4242. 大爷(w)&&CF1061E

题目描述:

这里有 $n$ 个节点和两位大爷,红大爷和蓝大爷。红大爷在坐在节点 $x$ 处,蓝大爷坐在节点 $y$ 处。然后他们各自画了 $n − 1$ 条边,形成了一棵红树和一棵蓝树。

现在大爷们想选择一些节点激活,激活第 $i$ 个节点会带来 $w_i$ 的收益,但是因为两位大爷的树长得不一样,所以他们要先商量一番。

两位大爷都分别开出了一些条件,条件是这样的,这位大爷画出的树上以该大爷所坐的节点为根,节点 $a_i$ 的子树中必须恰有 $b_i$ 个节点被激活。保证红大爷开出的条件中必存在$a_i = x$,同时蓝大爷开出的条件中必存在 $a_i = y$,不保证每位大爷开出的条件不会自相矛盾。如果没看懂请结合样例理解题意。

现在大爷们把你抓了起来,问你能否找到一个方案满足所有的条件,如果存在,输出最大的收益,否则输出 $-1$。

算法标签:费用流

思路:

考虑费用流,对于每一个点计算出最近能限制到我的节点

S连向红大爷的树里有限制的点,流量为限制点数(要减去子树内其他点的限制),费用为 $0$ 。蓝大爷的树里有限制的点连向T。

对于两颗树里的同一个节点,对于这个的节点在两棵树里限制自己的点之间连边,流量为 $1$ ,费用为 $-w[i]$ 。

这样求最小费用流,如果能满流即存在答案,且答案为最小费用的相反数。否则无解。

以下代码: 

#include<bits/stdc++.h>
#define il inline
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
const int N=1e3+5,inf=1e9;
bool vis[N];
int f[N<<3],c[N<<3],s1,s2,S,T,pre[N],ans,dis[N],w[N];
int n,rt1,rt2,head[N],ne[N<<3],to[N<<3],cnt,g[N],lk[N];
il int read(){
   int x,f=1;char ch;
   _(!)ch=='-'?f=-1:f;x=ch^48;
   _()x=(x<<1)+(x<<3)+(ch^48);
   return f*x;
}
il void ins(int x,int y){
    ne[++cnt]=head[x];
    head[x]=cnt;to[cnt]=y;
}
il int dfs(int x,int fa,int top){
    if(g[x])top=x;
    lk[x]=top;int res=0;
    for(int i=head[x];i;i=ne[i]){
        if(fa==to[i])continue;
        res+=dfs(to[i],x,top);
    }
    int tmp=g[x]?g[x]:res;
    if(g[x])g[x]-=res;
    if(g[x]<0){puts("-1");exit(0);}
    return tmp;
}
il void Add(int x,int y,int flow,int cost){
    ne[++cnt]=head[x];head[x]=cnt;
    to[cnt]=y;f[cnt]=flow;c[cnt]=cost;
}
il void add(int x,int y,int f,int c){
    Add(x,y,f,c);Add(y,x,0,-c);
}
il bool spfa(){
    for(int i=S;i<=T;i++)dis[i]=inf,vis[i]=0,pre[i]=-1;
    dis[S]=0;vis[S]=1;queue<int> q;q.push(S);
    while(!q.empty()){
        int x=q.front();q.pop();vis[x]=0;
        for(int i=head[x];i!=-1;i=ne[i]){
            if(dis[to[i]]>dis[x]+c[i]&&f[i]>0){
                dis[to[i]]=dis[x]+c[i];pre[to[i]]=i;
                if(!vis[to[i]])vis[to[i]]=1,q.push(to[i]);
            }
        }
    }
    return dis[T]<inf;
}
il void mcf(){
    while(spfa()){
        int mn=inf;
        for(int i=pre[T];i!=-1;i=pre[to[i^1]])
            mn=min(mn,f[i]);
        for(int i=pre[T];i!=-1;i=pre[to[i^1]])
            f[i]-=mn,f[i^1]+=mn;
        ans+=dis[T]*mn;s1-=mn;
    }
}
int main()
{
    n=read();rt1=read();rt2=read()+n;
    for(int i=1;i<=n;i++)w[i]=read();
    for(int i=1;i<n;i++){
        int x=read(),y=read();
        ins(x,y);ins(y,x);
    }
    for(int i=1;i<n;i++){
        int x=read()+n,y=read()+n;
        ins(x,y);ins(y,x);
    }
    int Q=read(),a,b;
    while(Q--)a=read(),g[a]=read();
    Q=read();
    while(Q--)a=read()+n,g[a]=read();
    dfs(rt1,0,rt1);dfs(rt2,0,rt2);
    cnt=1;S=0;T=(n<<1)+1;
    for(int i=S;i<=T;i++)head[i]=-1;
    for(int i=1;i<=n;i++){
        if(g[i])add(S,i,g[i],0),s1+=g[i];
        if(g[i+n])add(i+n,T,g[i+n],0),s2+=g[i+n];
    }
    for(int i=1;i<=n;i++)add(lk[i],lk[i+n],1,-w[i]);
    if(s1^s2)return puts("-1"),0;mcf();
    if(s1)return puts("-1"),0;
    printf("%d\n",-ans);
    return 0;
}
View Code

 

posted @ 2019-03-14 08:01  Jessiejzy  阅读(779)  评论(0编辑  收藏  举报