BZOJ3876: [Ahoi2014&Jsoi2014]支線劇情(洛谷P4043)
阿新 • • 發佈:2018-12-13
最小費用可行流
和有源匯上下界可行流一樣建,除了加邊權外沒什麼區別。因為可以在任意節點結束,所以要對每個節點建一條到超級匯點的容量為邊權為0的邊。
程式碼:
#include<queue>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 305
#define M 5005
#define F inline
using namespace std;
struct edge{ int nxt,to,v,d, f; }ed[M<<3];
struct fat{ int x,e; }fa[N];
int n,k=1,s,t,ss,tt,ans,h[N],d[N],r[N],a[N];
bool f[N]; queue <int> q;
F char readc(){
static char buf[100000],*l=buf,*r=buf;
if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
return l==r?EOF:*l++;
}
F int _read(){
int x=0; char ch=readc();
while (!isdigit (ch)) ch=readc();
while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
return x;
}
F void add(int x,int y,int z,int w){
if (x==0)
x=0;
ed[++k]=(edge){h[x],y,z,w},h[x]=k;
ed[++k]=(edge){h[y],x,0,-w},h[y]=k;
}
F int spfa(){
for (int i=ss;i<=tt;i++) d[i]=1e9,f[i]=false;
while (! q.empty()) q.pop();
for (q.push(ss),d[ss]=0,r[ss]=1e9;!q.empty();){
int x=q.front(); q.pop(),f[x]=false;
for (int i=h[x],v;i;i=ed[i].nxt)
if (ed[i].v>ed[i].f&&d[x]+ed[i].d<d[v=ed[i].to]){
d[v]=d[x]+ed[i].d,fa[v].e=i,fa[v].x=x;
r[v]=min(r[x],ed[i].v-ed[i].f);
if (!f[v]) q.push(v),f[v]=true;
}
}
return d[tt]==1e9?0:r[tt];
}
int main(){
n=_read(),s=1,t=n+1,tt=n+2;
for (int i=1,m;i<=n;i++){
m=_read(),a[i]-=m,add(i,t,1e9,0);
for (int v,d;m;m--)
v=_read(),d=_read(),a[v]++,ans+=d,add(i,v,1e9,d);
}
for (int i=1;i<=n;i++)
if (a[i]>0) add(ss,i,a[i],0);
else if (a[i]<0) add(i,tt,-a[i],0);
add(t,s,1e9,0);
for (int sum;sum=spfa();ans+=d[tt]*sum)
for (int x=tt,e;x!=ss;x=fa[x].x)
ed[e=fa[x].e].f+=sum,ed[e^1].f-=sum;
return printf("%d\n",ans),0;
}