题目描述:
这里有 $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 ",-ans); return 0; }