[BZOJ3876][网络流]AHOI2014:支线剧情

BZOJ3876

每条边下界为1,上界INF,然后就是最小费用最大流模板

Code:

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int res=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
    while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
    return res*f; 
}
const int N=10005,INF=0x3f3f3f3f;
int n,m,s,t,ans=0;
int vis[N<<1],cur[N<<1],tmp[N<<1],head[N<<1],nxt[N<<1],c[N<<1],e[N<<1],tot=1;
int pt[N],in[N],dis[N];
inline void add(int x,int y,int z,int cost){
    vis[++tot]=y;nxt[tot]=head[x];head[x]=tot;c[tot]=z;e[tot]=cost;
    vis[++tot]=x;nxt[tot]=head[y];head[y]=tot;c[tot]=0;e[tot]=-cost;
}
inline bool spfa(int S,int T){
    static queue<int>q;
    for(int i=0;i<=T;i++) dis[i]=INF,cur[i]=head[i],pt[i]=0;
    dis[S]=0;q.push(S);
    while(!q.empty()){
        int x=q.front();q.pop();pt[x]=0;
        for(int i=head[x];i;i=nxt[i]){
            int y=vis[i];
            if(!c[i] || dis[y]<=dis[x]+e[i]) continue;
            dis[y]=dis[x]+e[i];
            if(!pt[y]) pt[y]=1,q.push(y);
        }
    }
    return dis[T]!=INF;
}
int ver[N];
int dfs(int v,int T,int flow,int cost){
    ver[v]=1;
    if(v==T){ans+=flow*cost;return flow;}
    int res=0,k;
    for(int &i=cur[v];i;i=nxt[i]){
        int y=vis[i];
        if(ver[y] || dis[y]!=dis[v]+e[i] || !c[i]) continue;
        k=dfs(y,T,min(flow-res,c[i]),cost+e[i]);
        if(k){c[i]-=k;c[i^1]+=k;res+=k;if(res==flow) return res;}
    }
    dis[v]=INF;
    return res;
}
inline void dinic(int S,int T){
    while(spfa(S,T)){
        int t=0;
        memcpy(tmp,cur,sizeof(tmp));
        do{
            memcpy(cur,tmp,sizeof(cur));
            memset(ver,0,sizeof(ver));
            t=dfs(S,T,INF,0);
        }while(t!=0);
    }
}
int main(){
    n=read();m=n+1;s=m+1;t=s+1;
    for(int i=1;i<=n;i++){
        int tt=read();in[i]+=tt;
        for(int j=1;j<=tt;j++){
            int y=read(),z=read();
            add(i,y,INF,z);
            in[y]--;ans+=z;
        }
        add(i,m,INF,0);
    }
    add(m,1,INF,0);
    for(int i=1;i<=n;i++){
        if(in[i]>0) add(i,t,in[i],0);
        if(in[i]<0) add(s,i,-in[i],0);
    }
    dinic(s,t);
    cout<<ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值