P6122 [NEERC2016] Mole Tunnels

https://www.luogu.com.cn/problem/P6122

题目明显有一个匹配的问题,费用流的建模也很明显。

  • 考虑当前在某个时刻,睡醒的所有鼹鼠,都有一条 Si 的边,容量为 1,费用为 0
  • 对于树上一条边 u>v,注意,这里是把无向边看成两条单向边。连 uv,容量为无穷大,费用为 1。
  • 对于一个食物,连 iT,容量为 ci,费用为 0

这是一个匹配模型。对于任意时刻,要求所有 S>i 的边流满。

考虑模拟 EK 去找费用流。

加入一只鼹鼠 x,那么 Sx 要流满,前面的鼹鼠 ySy 也流满,且一定不会退流,因为如果退流的话,总流量减少,显然是不优的,因为我们每次是找费用最低的增广路,这个总流量是会单增的。

既然总流量单增,并且一定流满,那么对于一个负环来说,是不是原先的就并不会流满啊?并且负环能提供的流量增量为 0,因为要退掉之前的流量。那么,我增量的时候,找增广路,显然只会有一条,因为 Si 容量为 1。要求增广完网络满流,显然是不允许出现负环。

那么,我们只需要考虑 SxpT 这样子的源汇路径带来的增广。

注意到,我们一定是找一条费用最低的可流路径去增广,这是不是相当于,对于 x 找到距离它最近的粮食的点,并流过去。并且流过去后需要注意对路径上的所有反向边的流量增加 1。

那么问题很简单了。

  • 对于 x 找到距离它最近的粮食的点。

  • 增加一条权为 1 的边。

  • 减少一条权为 1 的边。

  • 减少某个点的粮食。

注意到,对于第一点,我们可以大力枚举两点的 LCA 做,因为树高 logn,那么就变成维护一个点,向下到粮食的距离了。

但是,减少对于某些并不满足求逆的东西来说,并不是很好做。我们考虑信息是合并上去的,这样子,我们规避了求逆这个东西。

我们发现,树的形态很特别,只有左右儿子,因此我们可以类线段树,完成对信息的合并。

接下来的东西都很常规了,就做完了。

#include <bits/stdc++.h> #define il inline #define ll long long #define pb push_back #define not_son(x) ((x)*2>n) #define is_ls(x) ((x)*2<=n) #define is_rs(x) ((x)*2+1<=n) using namespace std; const int N=(int)(1e5+5),inf=(int)(2e9),Lim=(int)(1e5); int n,m,c[N],up2[N],down2[N],dis[N],disp[N],ans; il void push_up(int x) { if(c[x]) dis[x]=0,disp[x]=x; else dis[x]=inf,disp[x]=0; if(is_ls(x)) { int ls=x*2; if(dis[ls]<inf) { if(down2[ls]) { if(dis[ls]-1<dis[x]) { dis[x]=dis[ls]-1; disp[x]=disp[ls]; } } if(dis[ls]+1<dis[x]) { dis[x]=dis[ls]+1; disp[x]=disp[ls]; } } } if(is_rs(x)) { int rs=x*2+1; if(dis[rs]<inf) { if(down2[rs]) { if(dis[rs]-1<dis[x]) { dis[x]=dis[rs]-1; disp[x]=disp[rs]; } } if(dis[rs]+1<dis[x]) { dis[x]=dis[rs]+1; disp[x]=disp[rs]; } } } } il void build(int x) { if(not_son(x)) { push_up(x); return ; } if(is_ls(x)) build(x*2); if(is_rs(x)) build(x*2+1); push_up(x); } #define another(x) (((x)&1)?((x)-1):((x)+1<=n)?((x)+1):0) #define cal_up(x) (up2[x]?(-1):1) #define cal_down(x) (down2[x]?(-1):1) il void fanxiang_up(int x,int y) { // cout<<x<<" "<<y<<'\n'; while(x!=y) { if(cal_up(x)==-1) { --up2[x]; } else { ++down2[x]; } push_up(x); x>>=1; } push_up(x); } il void fanxiang_down(int x,int y) { // cout<<x<<" down "<<y<<'\n'; while(x!=y) { if(cal_down(x)==-1) { --down2[x]; } else { ++up2[x]; } push_up(x); x>>=1; } push_up(x); } il void del(int p) { --c[p]; push_up(p); while(p) { p>>=1; if(!p) break ; push_up(p); } } signed main() { cin.tie(0); ios::sync_with_stdio(false); cin>>n>>m; for(int i=1;i<=n;i++) cin>>c[i]; build(1); for(int i=1;i<=m;i++) { int x; cin>>x; int P=x; int qwq=0,pos=0,res=inf,LCA=0; if(dis[x]<Lim) { res=dis[x]; pos=disp[x]; LCA=x; } while(1) { // cout<<x<<'\n'; int prex=x; x>>=1; if(!x) break ; qwq+=cal_up(prex); if(another(prex)) { int p=another(prex); if(dis[p]<Lim) { int v=qwq+cal_down(p)+dis[p]; if(v<res) { res=v; LCA=x; pos=disp[p]; } } } if(c[x]) { if(qwq<res) { res=qwq; LCA=x; pos=x; } } } ans+=res; cout<<ans<<' '; // cout<<"ans "<<ans<<'\n'; fanxiang_up(P,LCA); fanxiang_down(pos,LCA); del(pos); } return 0; }

__EOF__

本文作者F x o r G
本文链接https://www.cnblogs.com/xugangfan/p/17687022.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   FxorG  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
历史上的今天:
2022-09-08 记一道典题 分层图+同余系建图
点击右上角即可分享
微信分享提示