【模板】有源汇上下界最大流
妈的傻逼题解话不说清楚,不早说第二轮dinic的时候要去掉汇源边,害得我白白地多花费了一个小时。就离谱,这么一个网络流二次模板我写了一下午。晕了。
说回题目本身。建模是小事,主要是那个所谓的有源汇上下界最大流怎么求。
首先是无源汇上下界可行流怎么搞?可以想到假如强制让每一条边流满下界肯定是解题的一部分。而这样做显然会出现问题,比如假如一个点的前驱下界比你的上界都牛逼,你能怎么办,而这种情况是上述想法所考虑不到的。于是可以考虑在上述想法基础上给每个点维护一个w值,记录假如强制下界流满后每个节点的正负盈亏,正的代表流进来的太多了,反之就是库存空虚需要补给。于是现在问题就变成了如何让这些精力过剩的节点分配出来一些流量给那些可怜的小家伙呢?
众所周知图论中有一个超级源点和超级汇点的技巧。我们重新审视一下上面提出的那个问题:我们认为图中一些节点是可以向外进行贡献的,另一些节点是需要别人对它贡献的,而我们的目标是刚好补齐它们的差距使得每一个点都可以不多不少,这一点可以考虑建立超级源点,连接精力过剩的点;同时建立超级汇点,连接所有精力不足的点,然后跑最大流。如果最大流刚好等于所有点欠下的流量之和,则可以判定我们找到了一个可行流。
然后如何解决有源汇的问题呢?考虑连一条从汇点到源点(没有超级二字)边权极大的边,然后按照相同的方式跑最大流即可。
那最大流呢?可以想到在经历了上述两个步骤之后,我们得到了一个方案,保证每条边都达到了下限且每个节点都达到了收支平衡。接下来我们想让它的流尽量多,可以直接单纯滴跑最大流即可。需要注意的是上面那条从汇点连向源点的边在本次步骤中一定要删去,否则会死得很惨。我就是一个例子。
祭奠我平白逝去的一个下午。
#include<cstdio>
#include<cstring>
//#define zczc
using namespace std;
const int D=400;
const int M=1010;
const int C=310;
const int maxn=1e9;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
wh*=f;return;
}
inline int min(int s1,int s2){
return s1<s2?s1:s2;
}
int m,n,cnt;
struct edge{
int t,v,next;
}e[D*C*4];
int esum,head[D+M];
inline void adde(int fr,int to,int val){
e[++esum]=(edge){to,val,head[fr]};
head[fr]=esum;
}
inline void add(int fr,int to,int val){
adde(fr,to,val);
adde(to,fr,0);
}
int dt,d[D+M],q[D+M],l,r;
bool check(int s,int t){
for(int i=1;i<=cnt;i++)d[i]=0;
d[q[l=r=1]=s]=1;
while(l<=r){
int wh=q[l++];
for(int i=head[wh],th;i;i=e[i].next){
if(e[i].v&&d[th=e[i].t]==0)d[th]=d[wh]+1,q[++r]=th;
}
}
return d[t];
}
int dinic(int wh,int val,int t){
if(wh==t)return val;int cost=0;
for(int i=head[wh],th;i&&val;i=e[i].next){
if(e[i].v==0||d[th=e[i].t]!=d[wh]+1)continue;
int now=dinic(th,min(val,e[i].v),t);
e[i].v-=now,e[i^1].v+=now,cost+=now,val-=now;
}
return d[wh]=cost==0?0:d[wh],cost;
}
int pa[M];//每个妹子的编号
int pb[D];//每天的编号
int ss,tt;//可行流时的超级源汇点
int s,t,w[D+M];//原模型上的源汇点
void solve(){
int as=0,ans=0;
for(int i=1;i<=cnt;i++){
if(i==ss||i==tt)continue;
if(w[i]>0)add(ss,i,w[i]),as+=w[i];
else add(i,tt,-w[i]);
}
while(check(ss,tt))ans+=dinic(ss,maxn,tt);
if(ans!=as){printf("-1\n\n");return;}
e[3].v=e[2].v=0;
while(check(s,t))ans+=dinic(s,maxn,t);
printf("%d\n\n",ans);
}
void init(){
cnt=0,esum=1;
memset(head,0,sizeof(head));
memset(w,0,sizeof(w));
}
signed main(){
#ifdef zczc
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
while(~scanf("%d%d",&m,&n)){
init();
int num,li,s1,s2,s3;//那天有多少妹子以及最多拍照数
s=++cnt,t=++cnt,ss=++cnt,tt=++cnt;
add(t,s,maxn);
for(int i=1;i<=m;i++)pb[i]=++cnt;
for(int i=1;i<=n;i++)pa[i]=++cnt;
for(int i=1;i<=n;i++){
read(s1);
w[pa[i]]-=s1;
w[t]+=s1;
add(pa[i],t,maxn);
}
for(int i=1;i<=m;i++){
read(num);read(li);
add(s,pb[i],li);
while(num--){
read(s1);read(s2);read(s3);s1++;
w[pa[s1]]+=s2;
w[pb[i]]-=s2;
add(pb[i],pa[s1],s3-s2);
}
}
solve();
}
return 0;
}
一如既往,万事胜意