ACM ICPC 2017 WF Problem J Son of Pipe Stream 题解

89 篇文章 1 订阅

ACM ICPC 2017 WF Problem J Son of Pipe Stream

一个神仙网络流题。

首先一个直观的想法:可以枚举水的多少,然后让flubber尽量多。

然后再慢慢调整当前的策略。

假设当前的候选答案为 w , f w,f w,f。(表示水和flubber的数量)

若,当前在保证一个数不变的情况下可以增大另一个数,则这个一定不是最优解。

否则在增加一个数的同时另一个数会减小。可以发现一个数增加的数=另一个数减少的数。

所以两个数的总和不变。

另一个发现,两个加起来等于 m a x f l o w maxflow maxflow

不然一定可以在保证 s s s到其中一个源不退流而增广另一个。

然后就可以得到可行的备选最优解 ( w , f ) (w,f) (w,f)组成的直线。

然后可以发现那个函数是单峰的。然后就直接实数三分就好了。(避免精度误差,可以取 ln ⁡ \ln ln

code:

/*
{
######################
#       Author       #
#        Gary        #
#        2020        #
######################
*/
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
//inline int read(){
//    int x=0;
//    char ch=getchar();
//    while(ch<'0'||ch>'9'){
//        ch=getchar();
//    }
//    while(ch>='0'&&ch<='9'){
//        x=(x<<1)+(x<<3)+(ch^48);
//        ch=getchar();
//    }
//    return x;
//}
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}

*/
const int GRAPH_SIZE=250 ;
int pin[GRAPH_SIZE],dep[GRAPH_SIZE],s=0,t=GRAPH_SIZE-1;
struct EDGE{
	int u,v;
	double c;
};
vector<EDGE> e;
vector<int> each[GRAPH_SIZE];
bool bfs(){
	queue<int> Q;
	Q.push(s);
	while(!Q.empty()){
		int now=Q.front();
		Q.pop();
		for(auto it:each[now]){
			int next=e[it].v;
			if(e[it].c)
			if(dep[next]>dep[now]+1){
				dep[next]=dep[now]+1;
				Q.push(next);
			}
		}
	}
	return dep[t]!=INF;
}
double dfs(int now,double flow){
	if(now==t){
		return flow;
	}
	for(int & i= pin[now];i<each[now].size();i++){
		int it=each[now][i];
		if(e[it].c&&dep[e[it].v]==dep[now]+1){
			double tmp;
			if(tmp=dfs(e[it].v,min(double(flow),e[it].c))){
				e[it].c-=tmp;
				e[it^1].c+=tmp;
				return tmp;
			}
		}
	}
	return 0;
}
int Dinic(){
	int max_flow=0;
	rep(i,GRAPH_SIZE)
	{
		
		dep[i]=INF;
	}
	dep[s]=0;
	while(bfs()){
		rep(i,GRAPH_SIZE){
			pin[i]=0;
		}
		double tmp;
		while(tmp=dfs(s,INF)){
			max_flow+=tmp;
		}
		rep(i,GRAPH_SIZE){
			dep[i]=INF;
		}
		dep[s]=0;
	}
	return max_flow;
}
void make_edge(int U,int V,double C){
	EDGE tmp;
	tmp.u=U;
	tmp.v=V;
	tmp.c=C;
	e.PB(tmp);
	each[U].PB(e.size()-1);
	swap(tmp.u,tmp.v);
	tmp.c=0;
	e.PB(tmp);
	each[V].PB(e.size()-1);
}
void init(){
	e.clear();
	rep(i,GRAPH_SIZE){
		each[i].clear();
	}
}
int n,p;
double v,a;
double sum;
double water[100000],flubber[100000];
map<mp,int> is;
double calc(double x){
	return log(pow(x,a))+log(pow(sum-x,1.0-a));
}
void go(int now,double N,bool flub){
	if(N==0) return;
	for(auto it:each[now]){
		if(it&1) continue;
		if(e[it^1].c){
			double& z=e[it^1].c;
			int id=is[II(e[it].u,e[it].v)];
			double t=min(z,N);
			z-=t;
			N-=t;
			if(flub){
				if(id<0){
					id*=-1;
					flubber[id]-=t;
					go(e[it].v,t,flub);
				}
				else{
					flubber[id]+=t;
					go(e[it].v,t,flub);
				}
			}
			else{
				if(id<0){
					id*=-1;
					water[id]-=t;
					go(e[it].v,t,flub);
				}	
				else{
					water[id]+=t;
					go(e[it].v,t,flub);
				} 
			}
		}
	}
}
vector<pair<mp,int> > edges;
void construct(double f){
	init();
	for(auto it:edges){
		make_edge(it.FIR.SEC,it.FIR.FIR,it.SEC);
		make_edge(it.FIR.FIR,it.FIR.SEC,it.SEC);
	}
	make_edge(s,1,f);
	make_edge(s,2,sum-f);
	make_edge(3,t,INF);
	Dinic();
	go(1,f,1);
	go(2,sum-f,0);
}
int main(){
	scanf("%d%d",&n,&p);
	scanf("%lf%lf",&v,&a);
	rb(i,1,p){
		int j,k,c;
		scanf("%d%d%d",&j,&k,&c);
		edges.PB(II(II(j,k),c));
		is[II(j,k)]=i;
		is[II(k,j)]=-i;
		make_edge(j,k,c);
		make_edge(k,j,c);
		flubber[i]=water[i]=0.0;
	}
	make_edge(s,1,INF);
	make_edge(3,t,INF);
	Dinic();
	make_edge(s,2,INF);
	Dinic();
	int fm,wn,fn,wm;
	for(auto it:each[s]){
		if(e[it].v==1){
			fm=e[it^1].c;
		}
		else{
			wn=e[it^1].c;
		}
	}
	init();
	for(auto it:edges){
		make_edge(it.FIR.SEC,it.FIR.FIR,it.SEC);
		make_edge(it.FIR.FIR,it.FIR.SEC,it.SEC);
	}
	make_edge(s,2,INF);
	make_edge(3,t,INF);
	Dinic();
	make_edge(s,1,INF);
	Dinic();
	for(auto it:each[s]){
		if(e[it].v==1){
			fn=e[it^1].c;
		}
		else{
			wm=e[it^1].c;
		}
	}
	double rest=0.0;
	sum=fm+wn;
	double l,r;
	l=fn,r=fm;
	while(l+(1e-9)<r){
		double mid=(l+r)/2.0;
		if(calc(mid)<calc(mid+(1e-9))){
			l=mid+1e-9;
		}
		else{
			r=mid;
		}
	}
	construct(l);
	rest=pow(l,a)*pow(sum-l,1.0-a);
	rb(i,1,p){
		printf("%.10f %.10f\n",flubber[i]/v,water[i]);
	}
	printf("%.10f\n",rest*pow(v,-a));
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值