bzoj 3597: [Scoi2014]方伯伯运椰子

时间:2022-12-16 13:45:38

Description

 bzoj 3597: [Scoi2014]方伯伯运椰子

Input

 第一行包含二个整数N,M

接下来M行代表M条边,表示这个交通网络
每行六个整数,表示Ui,Vi,Ai,Bi,Ci,Di
接下来一行包含一条边,表示连接起点的边

Output

一个浮点数,保留二位小数。表示答案,数据保证答案大于0

Sample Input

5 10
1 5 13 13 0 412
2 5 30 18 396 148
1 5 33 31 0 39
4 5 22 4 0 786
4 5 13 32 0 561
4 5 3 48 0 460
2 5 32 47 604 258
5 7 44 37 75 164
5 7 34 50 925 441
6 2 26 38 1000 22

Sample Output

103.00

HINT

 1<=N<=5000

0<=M<=3000
 
1<=Ui,Vi<=N+2

0<=Ai,Bi<=500

0<=Ci<=10000

0<=Di<=1000
 
很久之前就想做这道题了...
上午叶大佬给我看了APIO2017 T3后,特别想找一道0/1分数规划打一打
这个题首先分数规划的思路是比较显然的...设答案为mid,wi为一次操作的贡献,n为次数;
X-Y>=mid*n;
X-(X+ wi)>=mid*n=mid* 1;
X-(X+ wi+mid)>=0;
wi+mid<=0;
那么我们就是需要判断 ∑wi+mid的最小值<=0;
首先可以很好的知道缩一条边的贡献是ai-di,扩大一条边的贡献是bi+di;
但是我们必须满足流量不变且每条边满载,
然后我发现修改一条边是牵一发而动全身的,为了一条边需要修改一连串的边且要一直到起点出来的那条边.
然后就觉得很完蛋...其实好像可以用判负圈那套理论...
于是在网上看到了一种很有道理的想法.是一种基于调整的思路.
假设先把所有的道路压缩为0,那么初始费用为:∑(ai-di+mid)*ci;
然后用费用流进行调整:
对于原图中的每条边进行两种调整:
1.连容量为c,费用为-(ai-di+mid)的边;
2.连容量为Inf,费用为(bi+di+mid)的边;(从起点出发的不连)
对于第一种边表示撤回原来的压缩操作,对于第二种边表示进行扩大操作.
 
这样做的正确性:
1.由于是最小费用最大流所以会优先增广第一种费用小的边,所以一定是把所有压缩操作撤回了,再考虑扩大操作的
2.由于是最小费用最大流,所以新图的最大流和原图相等,流量平衡了
妙不可言
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define RG register
using namespace std;
const int N=100000;
const int Inf=19260817;
const double eps=1e-5;
int head[N],nxt[N],to[N],s[N],fa[N],cnt=1,n,m,S,T,tt;
int w[N],ti[N],vis[N],q[N*10];
double c[N],dis[N],cost,ans;
struct data{
    int u,v,a,b,c,d;
}a[N];
inline void Addedge(RG int x,RG int y,RG int z,RG double v){
    to[++cnt]=y,nxt[cnt]=head[x],s[cnt]=z,c[cnt]=v,head[x]=cnt;
}
inline void lnk(RG int x,RG int y,RG int z,RG double v){
    Addedge(x,y,z,v);Addedge(y,x,0,-v);
}
inline bool spfa(){
    for(RG int i=1;i<=T;i++) vis[i]=0,dis[i]=Inf;
    int t=0,sum=1;q[0]=S,vis[S]=1,dis[S]=0;
    while(t<sum){
	int now=q[t++];vis[now]=0;
	for(RG int i=head[now];i;i=nxt[i]){
	    int y=to[i];
	    if(dis[y]>dis[now]+c[i]&&s[i]){
		fa[y]=i;dis[y]=dis[now]+c[i];
		if(!vis[y]) vis[y]=1,q[sum++]=y;
	    }
	}
    }
    if(abs(dis[T]-Inf)<=eps) return 0;
    int f=Inf;
    for(RG int i=fa[T];i;i=fa[to[i^1]]) f=min(f,s[i]);
    for(RG int i=fa[T];i;i=fa[to[i^1]]) s[i]-=f,s[i^1]+=f;
    cost+=dis[T]*f;
    return 1;
}
void rebuild(double mid){
    memset(head,0,sizeof(head));cnt=1;
    for(int i=1;i<=m;i++){
	lnk(a[i].u,a[i].v,a[i].c,-(-a[i].d+a[i].a+mid));
	if(a[i].u!=S) lnk(a[i].u,a[i].v,Inf,a[i].b+a[i].d+mid);
    }
}
bool check(double mid){
    rebuild(mid);double ret=0;cost=0;
    for(int i=1;i<=m;i++){
	ret+=(-a[i].d+a[i].a+mid)*a[i].c;
    }
    while(spfa());
    return 0-(ret+cost)>=eps;
}
int main(){
    scanf("%d%d",&n,&m);S=n+1,T=n+2;
    for(int i=1;i<=m;i++){
	scanf("%d%d%d%d%d%d",&a[i].u,&a[i].v,&a[i].a,&a[i].b,&a[i].c,&a[i].d);
    }
    double l=0,r=30000.0;
    while(r-l>=eps){
	double mid=(l+r)/2;
	if(check(mid)) ans=mid,l=mid;
	else r=mid;
    }
    printf("%.2f",ans);
    return 0;
}