BZOJ 3876 【AHOI2014】 支线剧情
题目链接:支线剧情
这道题就是一道裸裸的上下界网络流……只不过这道题边带了权,那么建出图之后跑费用流即可。
首先需要新建超级源\(S\)和超级汇\(T\)。对于这道题,对于一条边\((u,v,z)\),我们从\(S\)向\(v\)连一条容量为\(1\),费用为\(z\)的边,保证下界;从\(u\)向\(T\)连一条容量为\(1\),费用为\(0\)的边,也是用来保证下界的。由于没有上界,所以直接从\(u\)向\(v\)连一条容量为\(INF\),费用为\(z\)的边即可。
注意这道题可以在任意一个点重新开始,所以等于除\(1\)号点之外所有的点都可能成为汇点,所以连边的时候需要注意一下。也就是说,除了\(1\)号点之外的所有点,都要向\(1\)连一条容量为\(INF\),费用为\(0\)的边来保证流量平衡。
下面贴代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) #define maxn 310 #define maxm 50010 #define INF (1<<29) using namespace std; typedef long long llg; int n,S,T,c[maxm],f[maxm],ans; int head[maxn],next[maxm],to[maxm],tt=1; int d[maxn],dep[maxn],fr[maxn]; bool w[maxn]; int getint(){ int w=0;bool q=0; char c=getchar(); while((c>'9'||c<'0')&&c!='-') c=getchar(); if(c=='-') c=getchar(),q=1; while(c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w; } void link(int x,int y,int z,int o){ to[++tt]=y;next[tt]=head[x];head[x]=tt; to[++tt]=x;next[tt]=head[y];head[y]=tt; c[tt-1]=z; f[tt-1]=o; f[tt]=-o; } bool spfa(){ for(int i=1;i<=T;i++) dep[i]=INF; int ld=0,rd=0; dep[d[rd++]=S]=0; while(ld!=rd){ int u=d[ld++]; w[u]=0; if(ld==maxn) ld=0; for(int i=head[u],v;v=to[i],i;i=next[i]) if(c[i] && dep[v]>dep[u]+f[i]){ dep[v]=dep[u]+f[i]; fr[v]=i; if(!w[v]){ w[v]=1; if(dep[v]<=dep[d[ld]]) ld--,ld+=maxn,ld%=maxn,d[ld]=v; else d[rd++]=v,rd%=maxn; } } } return dep[T]!=INF; } int get(){ int now=INF,ans=0; for(int u=T;u!=S;u=to[fr[u]^1]) now=min(now,c[fr[u]]),ans+=f[fr[u]]; for(int u=T;u!=S;u=to[fr[u]^1]) c[fr[u]]-=now,c[fr[u]^1]+=now; return now*ans; } int main(){ File("a"); n=getint(); S=n+1,T=n+2; for(int i=1,x,u,z;i<=n;i++){ x=getint(); if(i!=1) link(i,1,INF,0); if(x) link(i,T,x,0); while(x--){ u=getint(); z=getint(); link(i,u,INF,z),link(S,u,1,z); } } while(spfa()) ans+=get(); printf("%d",ans); return 0; }