[jzoj 3302] 【集训队互测2013】供电网络 {上下界最小费用最大流}

题目

Description
阿狸和桃子居住的世界里, 只有一个国家, 这个国家有很多城市, 每个城市直接由中央政府管辖.
电力是这个国家的唯一能源, 但是每个城市的发电能力都不一样, 于是就产生了某些城市电力不足, 而某些城市却电力过剩的情况.
阿狸作为国家的首席工程师, 阿狸的一项重要工作就是均衡整个国家的电力, 使得每个城市的电力都恰好没有剩余或不足.
好在一些城市之间有电线可以输送电力, 这些电线都有自己的输送上限和下限, 并且输送电力的同时会产生大量的热.
每条电线i 发出的热量一定是关于输送电量的一个没有常数项的二次函数,即a_ix^2+b_ix, 并且由于电线是二极管做成的, 很显然只能单向输送电力. 每单位热量需要用1 单位的金币来冷却. 任何两个城市之间, 至多有一条电线.
不幸的是, 有时电力网络不像我们想的那么完美, 某些情况下可能无论如何都不能满足整个国家的电力需求. 这种情况下就只好向别的世界购买电力或者将电力输出给别的世界(注意, 每个城市的电力不能有剩余!), 每个城市买入或者输出电力的价格是不一样的(输出也要花钱).
由于阿狸的国家没有小数的概念, 输送,、购买或者交换电力都必须是以整数1 为单位.
阿狸的任务是最小化金币的花费(买入/送出的费用+电线上发热的冷却费用),他最近被这个问题搞得焦头烂额, 以至于没有时间去陪桃子玩, 结果天天被桃子骂T_T. 好在有你, 万能的程序猿, 请你编写一个程序来帮阿狸解决这个问题吧.

Input
第一行2 个整数, n 和m, 分别是城市个数和有向电线条数.
接下来n 行, 每行3 个整数[left, in, out], 其中第k 行表示第k 个城市的信息:
left 表示这个城市剩余(负数为不足)的电量, -5 <= left <= 5.
in, out 表示这个城市买入或送出一个单位电量的价格. 0 <= in, out <= 10000
最后m 行, 每行6 个整数, u, v, a, b, L, U 其中第k 行表示第k 条电线的信息:
u, v 分别表示电线的开始和结束城市(电力只能从u 输送到v).
a, b 表示电线发热的二次函数的二次项和一次项.
L, U 表示电线输送电力的下界和上界.

Output
仅一个整数, 表示最小的金币花费.


解题思路
zyc的详细见解


代码

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#define ll long long 
#define rep(i,x,y) for (register long long i=x;i<=y;++i)
using namespace std; 
const int N=210,M=610,inf=1000000; 
struct node{
	ll to,next,w,c; 
}a[M*20];
ll n,m,ls[N],s,e,tot=1,sum,left[N],cost[N][N],pre[N],f[N],x[M],y[M],A[M],B[M],L[M],U[M]; 
bool v[N]; queue<ll> q; 
void add(ll x,ll y,ll w,ll c){
	a[++tot]=(node){y,ls[x],w,c}; ls[x]=tot; 
	a[++tot]=(node){x,ls[y],0,-c}; ls[y]=tot; 
}
void change(){
	rep(i,1,m) 
	 if (cost[x[i]][y[i]]==L[i]&&L[i]<U[i])
	 L[i]++,add(x[i],y[i],1,A[i]*(2*L[i]-1)+B[i]); 
}
bool spfa(){
	change(); 
	memset(f,0x3f,sizeof(f)); 
	q.push(s); f[s]=0; v[s]=1; 
	while (!q.empty()){
		ll x=q.front(); q.pop(); v[x]=0; 
		for (ll i=ls[x];i;i=a[i].next){
			if (!a[i].w) continue; 
			ll y=a[i].to; 
			if (f[x]+a[i].c<f[y]){
				pre[y]=i; f[y]=f[x]+a[i].c; 
				if (!v[y]) q.push(y),v[y]=1;
			}
		}
	}
	return f[e]<=0; 
}
void update(){
	ll now=e; sum+=f[e]; 
	while (now!=s){
		ll x=a[pre[now]^1].to,y=a[pre[now]].to; 
		a[pre[now]].w--; a[pre[now]^1].w++; 
		cost[x][y]++; cost[y][x]--; 
		now=a[pre[now]^1].to; 
	}
}
signed main(){
//	freopen("1.txt","r",stdin); 
	scanf("%lld%lld",&n,&m); s=n+1,e=s+1; 
	rep(i,1,n) {
		ll in,out; 
		scanf("%lld%lld%lld",&left[i],&in,&out); 
		add(s,i,inf,in); 
		add(i,e,inf,out); 
	}
	rep(i,1,m){
		scanf("%lld%lld%lld%lld%lld%lld",&x[i],&y[i],&A[i],&B[i],&L[i],&U[i]); 
		left[x[i]]-=L[i]; left[y[i]]+=L[i]; 
		cost[x[i]][y[i]]+=L[i]; cost[y[i]][x[i]]-=L[i]; 
		sum+=A[i]*L[i]*L[i]+B[i]*L[i]; 
	}
	rep(i,1,n)
	if (left[i]>0) add(s,i,left[i],-inf); else add(i,e,-left[i],-inf); 
    while (spfa()) update(); 
	sum%=inf; if (sum<0) sum+=inf;  
	return 0&printf("%lld",sum);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值