IOI2021集训队作业 114DJ Son of Pipe Stream

一个双向网络中有两种液体,这两种液体之间可以混合或分离。在一个容量为(c)的管道中,两者流量((f,w))满足限制(fv+wle c),并且方向相同。分别有(f)(w)(1)(2),以及两者共同的汇点(3)。设((F,W))为从源点到汇点的流量,最大化(F^aW^{1-a})

(nle 305)


显然可以先忽略(v),最后再除以它。

显然如果有条边两种液体方向相反肯定是不优的。

分别从(1)(2)出发,求出各自的最大流(F_{max},W_{max}),再从连向(1)(2)的超级源出来,求出总的最大流(S)。有结论:对于解((F,W)),只要满足(Fle F_{max},Wle W_{max},F+Wle S),则一定有解。

谈谈我的理解:假设从超级源连出两条边,不断调整两条边的流量,使得流量总和为(S)

现在假如到(1)的边少了(epsilon),到(2)的边多了(epsilon)。那么从(2)开始的新的增广路一定和从(1)开始的原增广路有交,并且不能再找出一条从(1)开始的增广路使得可以补回(1)出发的(epsilon)

试题准备的题解的解释:有解((F_{max},S-F_{max}))((S-W_{max},W_{max}))(可以看做增广时优先增广其中一边),可以找到(tin[0,1]),得到((F,W)=t(F_{max},S-F_{max})+(1-t)(S-W_{max},W_{max}))

用点高中数学的知识求出最优时(F)是多少,然后还原方案。还原的时候可以先限制流量((F,W))跑一遍,得到每条边的合流量,把这个合流量变成容量,且把边定向,限制流量((F,0))跑一遍,就可以知道(f)的流量。


using namespace std;
#include <bits/stdc++.h>
#define N 305
#define INF 1000000000
int n,m;
double v,a;
struct edge{int u,v,c;} ed[N*N];
struct EDGE{
	int to;
	double c;
	EDGE *las;
} e[N*N];
int ne;
EDGE *last[N];
void link(int u,int v,double c){
	e[ne]={v,c,last[u]};
	last[u]=e+ne++;
}
#define rev(ei) (e+(int((ei)-e)^1))
int T;
EDGE *cur[N];
int dis[N],gap[N],BZ;
double dfs(int x,double s){
	if (x==T)
		return s;
	double have=0;
	for (EDGE *&ei=cur[x];ei;ei=ei->las)
		if (ei->c && dis[ei->to]+1==dis[x]){
			double t=dfs(ei->to,min(ei->c,s-have));
			ei->c-=t,rev(ei)->c+=t,have+=t;
			if (have==s)
				return s;
		}
	cur[x]=last[x];
	if (!--gap[dis[x]])
		BZ=0;
	++dis[x];
	++gap[dis[x]];
	return have;
}
void build(double c1=INF,double c2=INF){
	ne=0;
	memset(last,0,sizeof(EDGE*)*(n+2));	
	for (int i=0;i<m;++i){
		link(ed[i].u,ed[i].v,ed[i].c);
		link(ed[i].v,ed[i].u,ed[i].c);
	}
	link(n+1,1,c1),link(1,n+1,0);
	link(n+1,2,c2),link(2,n+1,0);
	T=3;
}
double flow(int S){
	memset(gap,0,sizeof gap);
	memset(dis,0,sizeof dis);
	gap[0]=n+1;
	memset(cur,0,sizeof cur);
	BZ=1;
	double res=0;
	while (BZ)
		res+=dfs(S,INF);
	return res;
}
double doit(int S,double c1=INF,double c2=INF){
	build(c1,c2);
	return flow(S);
}
pair<double,double> ans[N*N];
int main(){
//	freopen("in.txt","r",stdin);
	scanf("%d%d%lf%lf",&n,&m,&v,&a);
	for (int i=0;i<m;++i){
		int u,v,c;
		scanf("%d%d%d",&u,&v,&c);
		ed[i]={u,v,c};
	}
	double Fmx=doit(1);
	double Wmx=doit(2);
	double S=doit(n+1);
	double Fmn=S-Wmx,tmp=a*S,F,W;
	if (Fmn<=tmp && tmp<=Fmx)
		F=tmp;
	else if (tmp<Fmn)
		F=Fmn;
	else
		F=Fmx;
	W=S-F;
	doit(n+1,F,W);
	for (int i=0;i<m;++i){
		double f=ed[i].c-e[i*2].c;
		if (f>0)
			e[i*2].c=f,e[i*2+1].c=0;
		else
			e[i*2].c=0,e[i*2+1].c=-f;
		ans[i].second=f;
	}
	e[m*2].c=F,e[m*2+1].c=0;
	e[(m+1)*2].c=e[(m+1)*2+1].c=0;
	flow(n+1);
	for (int i=0;i<m;++i){
		double f;
		if (ans[i].second>0)
			ans[i].first=e[i*2+1].c;
		else
			ans[i].first=-e[i*2].c;
		ans[i].second-=ans[i].first;
	}
	for (int i=0;i<m;++i)
		printf("%lf %lf
",ans[i].first/v,ans[i].second);
	double val=pow(F/v,a)*pow(W,1-a);
	printf("%lf
",val);
	return 0;
}
原文地址:https://www.cnblogs.com/jz-597/p/14476410.html