【上下界网络流学习小计】JZOJ3302.供电网络
Description
每个城市i多(缺)电量Left[i],可以耗费in[i]从外面输入电量,out[i]向外面输出电量,或者有一些有向边可以转移电量,这些有向边的流量有上界和下界,代价为a*x2+b,x为流量。求最少满足电量刚刚好为0的方案数。
Solution
- 显然的上下界费用流题目。
- 先考虑建图,再考虑上下界的问题。
- 设源点S,汇点T。S向i或i向T连一个上界和下界都为|left[i]|的边,表示初值为此。
- 源点向i连边,表示in[i],i向汇点连边,表示out[i]。
- 原图中的有向边再根据上下界和费用连,将二次的代价拆一拆,则每流一次费用要改为a+b,3a+b,5a+b,所以要动态改路径上的边的费用。
- 最后考虑上下界的问题。
上下界费用流
- 网上有很多资料将上下界可行流讲的十分详细,但是上下界费用流的做法却缺少一些适合我的理解。
- 对于上下界可行流,可以参考(https://www.cnblogs.com/liu-runda/p/6262832.html)
(大佬%%%)的理解和做法,即将下界所构成的网络和剩下的残量网络分开,根据每个点的入度和出度之差向超级源或超级汇连边,计算附加流,再将流量合并。 - 如果有源汇就从汇点向源点连一条inf的边,使它变成无源汇。
- 这样子连边边数自然比较少,但是我们注意到对于费用流,每一条边的费用是不一样的,难以将入度出度抵消,所以以上的做法并不适用。
- 这题有另一种神奇的连法,然后跑最小费用最大流:有边x->y下界a,上界b。则改为连为ss->y[a],x->y[b-a],x->tt[a]。[x]表示流量是x。
- ss->y和x->tt确定了下界为a,只有当ss以及tt都满流的时候才有可行流。
- x->y确定了上界和下界之间的部分的贡献。
- 为什么这样是对的?
其实我也不会- 考虑如果有一条边,对于u,v的出度和入度分别有贡献,我们要求最后的图每一个点(除了超级源和超级汇)的出度都等于入度 (这样才能平衡)
- 设定当且仅当ss的出边全部满流,tt的入边全部满流时,有可行流。
- 那么每一条ss的出边都是下限,将y的入度+a;每一条tt的入边都是下限,将x的出度+a。这入度和出度的对应关系刚好同于x->y这条边对于入度出度造成的影响。再考虑x->y的b-a的边,每走一次就说明这条边在原图中的流量大了1。
- 总之最终如果有可行流必定使得x和y的出度入度分别为下限到上限之间,而流量能够守恒,所以满足了条件。
关于哪条边有费用
- 由于ss->y和x->tt必经过并且流量相等,可以假定它们一定是同时经过的,所以这两条边中只有一条有费用。
- 由于x->y[b-a]表示的是在下限以上的多出来的部分,所以它的费用是跟下限部分的费用独立的,所以这条边也要有费用。
- 另外,这道题的费用随着流量改变,所以ss->y的费用为流量在下限以内的费用,从a+b开始依次递增。但是x->y的费用已经假定走过了下限,所以从(2*low+1)*a+b开始递增。
这题的一些细节
- 反向边的建立:反向边的费用要比正向边费用小一个2a,因为正向边还没有加,所反向边只能删去上一个正向边已经加过了的,所以小2a。
- 走反向边之后费用不再+2a,因为反向边同于撤销操作,所以费用应该-2a,即还原回去。
- 费用流我打的是EK,比较慢,但是方便每一次修改边的费用。
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define maxn 205
#define maxm 10005
#define maxd 1005
#define inf 2e9
#define I(x) ((x&1)?x+1:x-1)
using namespace std;
int n,m,i,j,k,x,y,a,b,L,U,left[maxn],in[maxn],out[maxn],ans;
int S,T,SS,TT;
int em,e[maxm],nx[maxm],ls[maxn],ec[maxm],va[maxm],vb[maxm],ct[maxm],tp[maxm];
int dis[maxn],vis[maxn],d[maxd],fa[maxn],fai[maxn],t,w;
void insert(int x,int y,int flow,int a,int b,int cnt){
em++; e[em]=y; nx[em]=ls[x]; ls[x]=em; ec[em]=flow; va[em]=a; vb[em]=b; ct[em]=cnt; tp[em]=1;
em++; e[em]=x; nx[em]=ls[y]; ls[y]=em; ec[em]=0; va[em]=a; vb[em]=b; ct[em]=cnt-2; tp[em]=-1;
}
void link(int x,int y,int low,int up,int v){
insert(x,y,up-low,0,v,1);
insert(SS,y,low,0,v,1);
insert(x,TT,low,0,0,1);
}
int cost(int i){return (va[i]*ct[i]+vb[i])*tp[i];}
int spfa(){
memset(dis,127,sizeof(dis));
memset(vis,0,sizeof(vis));
memset(fa,0,sizeof(fa));
t=0,w=1;
d[1]=TT,vis[TT]=1,dis[TT]=0;
while (t<w){
t=(t+1)%maxd;
int x=d[t]; vis[x]=0;
for(int i=ls[x];i;i=nx[i]) if (ec[I(i)]&&dis[x]+cost(I(i))<dis[e[i]]){
dis[e[i]]=dis[x]+cost(I(i));
fa[e[i]]=x; fai[e[i]]=i;
if (!vis[e[i]]) w=(w+1)%maxd,vis[e[i]]=1,d[w]=e[i];
}
}
return dis[SS]<inf;
}
void EKflow(){
ans=0;
while (spfa()){
memset(vis,0,sizeof(vis));
int x;
for(x=SS;x!=TT&&!vis[x];x=fa[x]) {
vis[x]=1;
int i=fai[x],j=I(i);
ec[i]++,ct[i]+=2*tp[j];
ec[j]--,ct[j]+=2*tp[j];
}
ans+=dis[SS];
}
}
int main(){
scanf("%d%d",&n,&m);
S=n+1,T=n+2,SS=n+3,TT=n+4;
for(i=1;i<=n;i++) {
scanf("%d%d%d",&left[i],&in[i],&out[i]);
if (left[i]>=0) link(S,i,left[i],left[i],0);
else link(i,T,-left[i],-left[i],0);
link(S,i,0,inf,in[i]);
link(i,T,0,inf,out[i]);
}
for(i=1;i<=m;i++) {
scanf("%d%d%d%d%d%d",&x,&y,&a,&b,&L,&U);
insert(SS,y,L,a,b,1);
insert(x,y,U-L,a,b,2*L+1);
insert(x,TT,L,0,0,1);
}
insert(T,S,inf,0,0,1);
EKflow();
printf("%d",ans);
}