[SCOI2014]方伯伯运椰子

Posted by Dispwnl on March 15, 2018

题目

题目描述

四川的方伯伯为了致富,决定引进海南的椰子树。方伯伯的椰子园十分现代化,椰子园中有一套独特的交通系统。

现在用点来表示交通节点,边来表示道路。这样,方伯伯的椰子园就可以看作一个有n+2个交通节点,m条边的有向无环图。n+1号点为入口,n+2号点为出口。

每条道路都有6个参数,ui,vi,ai,bi,ci,di,

分别表示,该道路从ui号点通向vi号点,将它的容量压缩一次要ai的花费,容量扩大一次要bi的花费,该条道路当前的运输容量上限为ci,并且每单位运输量通过该道路要di的费用。

在这个交通网络中,只有一条道路与起点相连。因为弄坏了这条道路就会导致整个交通网络瘫痪,聪明的方伯伯决定绝不对这条道路进行调整,也就是说,现在除了这条道路之外,对其余道路都可以进行调整。

有两种调整方式:

选择一条道路,将其进行一次压缩,这条道路的容量会下降1单位。

选择一条道路,将其进行一次扩容,这条道路的容量会上升1单位。

一条道路可以被多次调整。

由于很久以前,方伯伯就请过一个工程师,对这个交通网络进行过一次大的优化调整。所以现在所有的道路都被完全的利用起来了,即每条道路的负荷都是满的(每条道路的流量等于其容量)。

但方伯伯一想到自己的海南椰子会大丰收,就十分担心巨大的运输量下,会导致过多的花费。因此,方伯伯决定至少进行一次调整,调整之后,必须要保持每条道路满负荷,且总交通量不会减少。

设调整后的总费用是Y,调整之前的总费用是X。现在方伯伯想知道,最优调整比率是多少,即假设他进行了k次调整,(X-Y)/k最大能是多少?

注:总费用=交通网络的运输花费+调整的花费

输入输出格式

输入格式:

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

输出格式:

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

输入输出样例

输入样例#1:

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

输出样例#1:

103.00

说明

1<=n<=5000,0<=m<=3000,1<=ui,vi<=n+2,0<=ai,bi<=500,0<=ci<=10000,0<=di<=1000

题解

感谢$fuge$帮我理解了最后一步

这题神TM标签网络流似乎用到了网络流的思想……

首先,这题肯定是分数规划,因为ta要求调整之后必须要保持每条道路满负荷,且总交通量不会减少

如果总交通量增加,花费肯定会增加,即大于调整前的花费

所以要保持流量不变,我们考虑网络流的思想

压缩$\rightarrow$网络流里的退流

扩充$\rightarrow$网络流里的增广

所以对于原图给定每一条边$(u,v)$,我们在新图里对应建两条边

$(v,u)$,边权为$a_i-d_i$(你压缩一单位花费$a_i$,同时这条边少了一单位流量所以花费减去$d_i$),对应压缩

$(u,v)$,边权为$b_i+d_i$(你扩充一单位花费$b_i$,同时这条边多了一单位流量所以花费加上$d_i$),对应扩充

我们令$tot=max(\frac{X-Y}{k})$,所以$tot\ge \frac{X-Y}{k}$

即$tot\times k\ge X-Y$

所以我们二分一个数$mid$,即调整比率

我们可以发现,在新图中xjb跑,经过的点数(也是边数)就是调整次数,即$k$

因为你调整一次就相当于经过新图里的一条边,同时加上这条边的贡献$w$(就是边权)

我们可以把$X-Y$化为$X-(X+\sum w)$,即$-\sum w$

所以问题就转为了比较$mid\times k$和$-\sum w$的大小

即$mid\times k+\sum w$是否大于0

然后原问题就转化为在新图里找负环

要是有负环,说明$mid\times k<X-Y$

反之说明$mid\times k\ge X-Y$

这样问题就解决了

还有一点,题目中要求不调整源点连接的那一条边,所以特判一下但没特判好像也能过

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<queue>
using namespace std;
const int MAX=1e4+1;
const double ops=1e-3;
struct p{
	int x,y;
	double dis;
}c[MAX];
int n,m,num;
int h[MAX];
double d[MAX];
bool use[MAX];
int read()
{
	int x=0;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar());
	for(;isdigit(ch);x=x*10+ch-48,ch=getchar());
	return x;
}
void add(int x,int y,double dis)
{
	c[++num]=(p){h[x],y,dis},h[x]=num;
}
bool spfa(int x,double mid)
{
	use[x]=1;
	for(int i=h[x];i;i=c[i].x)
	  if(d[c[i].y]>d[x]+c[i].dis+mid)
	  {
	  	d[c[i].y]=d[x]+c[i].dis+mid;
	  	if(use[c[i].y]) return 1;
	  	if(spfa(c[i].y,mid)) return 1;
	  }
	use[x]=0;
	return 0;
}
bool look(double mid)
{
	memset(d,0,sizeof(d));
	memset(use,0,sizeof(use));
	for(int i=1;i<=n+2;i++)
	  if(spfa(i,mid)) return 1;
	return 0;
}
int main()
{
	n=read(),m=read();
	for(int i=1;i<=m;i++)
	  {
	  	int x=read(),y=read();
		double A=read(),B=read(),dis=read(),cn=read();
	  	if(x==n+1) A=0,B=0,cn=0;
		if(dis) add(y,x,A-cn);
	  	add(x,y,B+cn);
	  }
	double l=0,r=1e8,ans;
	while(l<=r)
	{
		double mid=(l+r)/2;
		if(look(mid))
		ans=mid,l=mid+ops;
		else r=mid-ops;
	}
	printf("%.2lf",ans);
	return 0;
}