• 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;
    }
    
  • 相关阅读:
    DLL库
    C#:时间转换
    视频通信网址
    C#:向exe传值
    C#:复杂条件判断类型(练习)
    注册、卸载DLL
    Windows:常见问题
    WPF:常见问题
    CentOS完美搭建Redis3.0集群并附测试
    xargs命令
  • 原文地址:https://www.cnblogs.com/jz-597/p/14476410.html
Copyright © 2020-2023  润新知