洛谷 P6938 - [ICPC2017 WF]Son of Pipe Stream(网络流)
见过的最怪的网络流题,没有之一。
首先新建超级源点,向 各连 的边。设最大流为 ,那么显然最优方案中 flutter 和 water 流量之和为 。
先分析一波答案函数。显然,最终答案关于 flutter 的流量 的函数 。求导得 ,化简得 ,解得 在 上单调递增, 上单调递减。当然,有时候 并不能恰好取到 —— 显然 flutter 流量上界有最大值 ,water 流量上界也有最大值 ,求出 之后找 中距离 最近的点的位置即可,下文中记这个位置为 。(当然这一步也可以三分,因为代价函数存在严格凸性)
然后很自然的想法是直接以 为源点 为汇点跑流量限制为 的最大流,但是这样并不能保证 F 和 W 流量方向相同。考虑调整:先在上面的残量网络中找出每条边的方向,然后只保留这个方向的边,双向改单向,流量为残量网络上这条边已经用掉的流量,然后再跑限流 的最大流,这样每条边的流量就是 F 的流量,剩余容量就是 W 的流量,感性理解一下保留最大流中的边不影响 到 的最大流,因此我们的构造是正确的。
const int MAXN=300; const int MAXM=5e4; const double INF=1e9; int n,m;double v,a; struct edge{int u,v;double w;}e[MAXM+5]; int S,T,hd[MAXN+5],to[MAXM*2+5],nxt[MAXM*2+5],ec=1;double cap[MAXM*2+5]; void clear(){memset(hd,0,sizeof(hd));ec=1;} void adde(int u,int v,double f){ to[++ec]=v;cap[ec]=f;nxt[ec]=hd[u];hd[u]=ec; to[++ec]=u;cap[ec]=f;nxt[ec]=hd[v];hd[v]=ec; } int dep[MAXN+5],now[MAXN+5]; bool getdep(){ memset(dep,-1,sizeof(dep));queue<int>q;q.push(S);dep[S]=0; while(!q.empty()){ int x=q.front();q.pop();now[x]=hd[x]; for(int e=hd[x];e;e=nxt[e]){ int y=to[e];double z=cap[e]; if(!~dep[y]&&z>0)dep[y]=dep[x]+1,q.push(y); } }return ~dep[T]; } double getflow(int x,double f){ if(x==T)return f;double ret=0; for(int &e=now[x];e;e=nxt[e]){ int y=to[e];double z=cap[e]; if(dep[y]==dep[x]+1&&z>0){ double w=getflow(y,min(f-ret,z)); ret+=w;cap[e]-=w;cap[e^1]+=w; if(f==ret)return ret; } }return ret; } double dinic(){ double ret=0; while(getdep())ret+=getflow(S,INF); return ret; } void readd_graph(){clear();for(int i=1;i<=m;i++)adde(e[i].u,e[i].v,e[i].w);} double Fmax,Wmax,Z,ndF,ndW;int dir[MAXM+5]; int main(){ freopen("transport.in","r",stdin); freopen("transport.out","w",stdout); scanf("%d%d%lf%lf",&n,&m,&v,&a); for(int i=1;i<=m;i++)scanf("%d%d%lf",&e[i].u,&e[i].v,&e[i].w); readd_graph();S=1;T=3;Fmax=dinic(); readd_graph();S=2;T=3;Wmax=dinic(); readd_graph();S=n+1;T=3;adde(S,1,INF);adde(S,2,INF);Z=dinic(); if(Z-Wmax<=a*Z&&a*Z<=Fmax)ndF=a*Z; else if(Fmax<a*Z)ndF=Fmax; else ndF=Z-Wmax; ndW=Z-ndF; readd_graph();S=n+1;T=3;adde(S,1,ndF);adde(S,2,ndW);dinic(); for(int i=2;i<=ec;i+=2){ double mid=(cap[i]+cap[i^1])/2; if(cap[i]>mid)dir[i>>1]=-1,cap[i^1]=mid-cap[i^1],cap[i]=0; else dir[i>>1]=1,cap[i]=mid-cap[i],cap[i^1]=0; } for(int e=hd[n+1];e;e=nxt[e]){ int y=to[e]; if(y==1)cap[e]=ndF,cap[e^1]=0; else cap[e]=cap[e^1]=0; } dinic(); for(int i=2;i<=ec-4;i+=2){ if(dir[i>>1]==1)printf("%.10lf %.10lf\n",cap[i^1]/v,cap[i]); else printf("-%.10lf -%.10lf\n",cap[i]/v,cap[i^1]); } double res=pow(ndF/v,a)*pow(ndW,1-a); printf("%.10lf\n",res); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
2021-05-01 行列式小记
2021-05-01 Contest 2050 and Codeforces Round #718 (Div. 1 + Div. 2) 题解