一看这题感觉要分数规划,于是迅速上套路:
二分一个答案lim,如果存在
,则存在
然后发现压缩相当于网络流中的退流,扩容则相当于增广。当然也可以本质的理解,一开始所有点上的“囤积流量”都是0,如果压缩,那么v的“囤积流量”-1,u的“囤积流量”+1。扩容则反之。最后搞了一通后的目标状态也是所有点的“囤积流量”都是0,这样就可以看作把一个数字-1不断地从这个点移动到那一个点,最后要移回那个一开始被+1的点。
那么就可以考虑这样一个模型,连边(u,v,b+d)和(v,u,a-d),分别表示扩容后的费用变化和压缩后的费用变化。
又发现修改次数就是经过了多少条边,也就是边权可以再同时减去lim,求是否存在正权环。
正权环我不知道怎么求,但是可以把所有边权取相反数求有没有负权环(或权值为0的环)。
你以为这样就结束了?并没有,显然每条边可以进行压缩的量是有限制的。
但是经过网络流的思想思考,退流增广一次和退流增广若干次并不改变正负性,所以每一次退流或增广都只要进行一次。因此我们只要对于c=0的情况,不建立表示压缩的边即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
int q=0;char ch=' ';
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
return q;
}
typedef double db;
const db eps=1e-9;
const int N=5005,M=6005;
int n,m,tot,S,T;
int h[N],ne[M],to[M],inq[N],num[N];db w[M],dis[N];
void add(int x,int y,db z) {to[++tot]=y,ne[tot]=h[x],h[x]=tot,w[tot]=-z;}
int spfa(db lim) {
queue<int> q;
for(RI i=1;i<=n;++i) q.push(i),inq[i]=num[i]=1,dis[i]=0;
while(!q.empty()) {
int x=q.front();q.pop(),inq[x]=0;
for(RI i=h[x];i;i=ne[i])
if(dis[x]+lim-w[i]<=dis[to[i]]+eps) {
dis[to[i]]=dis[x]+lim-w[i];
if(!inq[to[i]]) {
inq[to[i]]=1,++num[to[i]],q.push(to[i]);
if(num[to[i]]==n) return 1;
}
}
}
return 0;
}
int main()
{
db l=0,r=1e7;int x,y,a,b,c,d;
n=read()+2,m=read();
for(RI i=1;i<=m;++i) {
x=read(),y=read(),a=read(),b=read(),c=read(),d=read();
add(x,y,b+d);
if(c!=0) add(y,x,a-d);
}
while(fabs(r-l)>1e-3) {
db mid=(l+r)/2.0;
if(spfa(mid)) l=mid;
else r=mid;
}
printf("%.2lf\n",l);
return 0;
}