题目描述
阿狸和桃子居住的世界里, 只有一个国家, 这个国家有很多城市, 每个城市直接由中央政府管辖.
电力是这个国家的唯一能源, 但是每个城市的发电能力都不一样, 于是就产生了某些城市电力不足, 而某些城市却电力过剩的情况.
阿狸作为国家的首席工程师, 阿狸的一项重要工作就是均衡整个国家的电力, 使得每个城市的电力都恰好没有剩余或不足.
好在一些城市之间有电线可以输送电力, 这些电线都有自己的输送上限和下限, 并且输送电力的同时会产生大量的热.
每条电线i 发出的热量一定是关于输送电量的一个没有常数项的二次函数,即a_i*x^2+b_i*x, 并且由于电线是二极管做成的, 很显然只能单向输送电力. 每单位热量需要用1 单位的金币来冷却. 任何两个城市之间, 至多有一条电线.
不幸的是, 有时电力网络不像我们想的那么完美, 某些情况下可能无论如何都不能满足整个国家的电力需求. 这种情况下就只好向别的世界购买电力或者将电力输出给别的世界(注意, 每个城市的电力不能有剩余!), 每个城市买入或者输出电力的价格是不一样的(输出也要花钱).
由于阿狸的国家没有小数的概念, 输送,、购买或者交换电力都必须是以整数1 为单位.
阿狸的任务是最小化金币的花费(买入/送出的费用+电线上发热的冷却费用),他最近被这个问题搞得焦头烂额, 以至于没有时间去陪桃子玩, 结果天天被桃子骂T_T. 好在有你, 万能的程序猿, 请你编写一个程序来帮阿狸解决这个问题吧.
最小费用可行流
建图显然:
每个城市建一个点,如果富余j的电量s向i连下限j上限j费用0的边,如果缺失j的电量i向t连下限j上限j费用0的边。向外国买电,s向i连下限0上限inf费用为买电费用的边,向外国送电,i向t连下限0上限inf费用为送电费用的边。对于输电线u向v,是一个凸费用边。一般凸费用边ax^2+bx,上限为L,我们都习惯拆为L条边,容量均为1,费用分别为a+b,3a+b,5a+b……而本题还有下限,u到v下限为拆边方法见我的上下界网络流学习小计,所以ss到v也要连一条凸费用的边,然后u到v的边容量变成r-l了,然而其的费用不是从a+b开始的。所以实现时,每条边保存初始费用以及增减系数(注意是增减系数而不是二次项系数,因为反向弧和正向弧的增减系数是一样的,可以自己思考一下为什么),每次一条边容量-1时,其费用就加上两倍的增减系数,类似容量+1时其费用就减去两倍的增减系数。然后所有的边都能表示为凸费用边的形式(即使其费用函数是一次函数或常数函数),然后这样的实现其实就是动态加边。但是要注意一下反向弧的初始费用以及ss到v的初始费用。详见代码。
然后就是求最小费用可行流了,相当于ss到tt跑最小费用最大流,这里最大流的限制是针对满足下限的,而原图没有流量限制,所以不会出现一个城市不断向外国买电然后向外国送电的情况(简单来说,买电和送电不是必须的,他们下限为0,原图最大流为无穷大,而新图不需要满足他们的流量限制,因为新图最大流不对应原图最大流)
还有一种做法,是把有下限的边的费用拉得很小,按照最小费用的限制就会被强制跑,然后用spfa(zkw不能处理正向弧为负费用的情况)增广直的费用》0。由于我没打过,这里不多说。
#include<cstdio>
#include<iostream>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=200+10,maxm=(600+maxn*3)*2*3+10,inf=1000000000;
int h[maxn],now[maxn],d[maxn],go[maxm],next[maxm],dis[maxm],L[maxm],co[maxm],fx[maxm],ax[maxm];
bool bz[maxn],czy;
int i,j,k,l,r,u,v,s,t,n,m,tot,ans,ss,tt,tmp;
void add(int x,int y,int z,int c,int a,int d){
go[++tot]=y;
dis[tot]=z;
co[tot]=c;
ax[tot]=a;
fx[tot]=tot+d;
next[tot]=h[x];
h[x]=tot;
}
void link(int x,int y,int l,int r,int a,int b){
add(ss,y,l,a+b,a,1);
add(y,ss,0,a-b,a,-1);
add(x,tt,l,0,0,1);
add(tt,x,0,0,0,-1);
add(x,y,r-l,(2*l+1)*a+b,a,1);
add(y,x,0,(1-2*l)*a-b,a,-1);
}
int dfs(int x,int cost){
bz[x]=1;
if (x==tt){
ans+=cost;
return 1;
}
int r=now[x];
while (r){
if (!bz[go[r]]&&dis[r]&&d[x]==d[go[r]]+co[r]){
if (dfs(go[r],cost+co[r])){
dis[r]--;
co[r]+=2*ax[r];
dis[fx[r]]++;
co[fx[r]]-=2*ax[fx[r]];
now[x]=r;
return 1;
}
}
r=next[r];
}
now[x]=0;
return 0;
}
bool change(){
int tmp=inf,i,r;
fo(i,ss,tt)
if (bz[i]){
r=h[i];
while (r){
if (!bz[go[r]]&&dis[r]&&d[go[r]]+co[r]-d[i]<tmp) tmp=d[go[r]]+co[r]-d[i];
r=next[r];
}
}
if (tmp==inf) return 0;
fo(i,ss,tt)
if (bz[i]) d[i]+=tmp;
return 1;
}
int main(){
scanf("%d%d",&n,&m);
ss=1;tt=n+4;
s=2;t=n+3;
fo(i,1,n){
scanf("%d%d%d",&j,&k,&l);
if (j>0) link(s,i+2,j,j,0,0);else link(i+2,t,-j,-j,0,0);
link(s,i+2,0,inf,0,k);
link(i+2,t,0,inf,0,l);
}
fo(i,1,m){
scanf("%d%d%d%d%d%d",&j,&k,&l,&r,&u,&v);
link(j+2,k+2,u,v,l,r);
}
link(t,s,0,inf,0,0);
do{
fo(i,ss,tt) now[i]=h[i];
fill(bz+ss,bz+tt+1,0);
while (dfs(ss,0)) fill(bz+ss,bz+tt+1,0);
}while (change());
printf("%d\n",ans);
}