P5470-[NOI2019]序列【模拟费用流】

1|0正题

题目链接:https://www.luogu.com.cn/problem/P5470


1|1题目大意

两个长度为n的序列a,b,求出它们两个长度为K的子序列,且这两个子序列至少有L个位置下标相等。

求最大化两个子序列的和。
T10,1n2×105,n106


1|2解题思路

先考虑一个简单的费用流做法,我们对于ai建在左边,bi建在右边。

然后开设一条流量为KL的边供所有ai连接所有bi表示这些可以随便匹配,然后剩下的都是ai连接bi就好了。

但是费用流显然会T,所以考虑一下怎么模拟这个过程。

记流KL的为自由流,其他的为限制流,我们肯定会把自由流先流满,模拟的过程就是每次选最大的两个匹配,当然如果这两个恰好是一起的就流限制流就好了。

自由流流满之后有三种操作

  1. 正常流限制流,选取最大的一个两边都未匹配的流过去
  2. 寻找增广路,我们肯定是会退流一条自由流匹配的,此时我们需要找到一个限制流。这里拿a举例,也就是我们需要找到匹配过的aibi未匹配,此时我们增广ai>bi,那么之前与ai匹配的就需要找一个最大的bi匹配。b同理。

但是这样会发现还是有问题,比如有时我们会出现ax<>by,bx<>ay的情况,此时相当与从ax/y<>bx/y增广。

每次匹配完之后判断一下这种情况就好了。开5个堆维护即可

时间复杂度O(nlogn)


1|3code

#include<cstdio> #include<cstring> #include<algorithm> #include<queue> #define mp(x,y) make_pair(x,y) #define val first #define id second using namespace std; const int N=2e5+10; int T,n,K,L,Free,a[N],b[N],link[N],rink[N]; priority_queue<pair<int,int> >H,Ano,Bno,Ause,Buse; long long ans; void Change(int x){ if(link[x]&&rink[x]&&link[x]!=x){ if(link[x]==rink[x])Free++; rink[link[x]]=rink[x]; link[rink[x]]=link[x]; link[x]=rink[x]=x;Free++; } return; } int main() { scanf("%d",&T); while(T--){ memset(link,0,sizeof(link)); memset(rink,0,sizeof(rink)); while(!H.empty())H.pop(); while(!Ano.empty())Ano.pop(); while(!Bno.empty())Bno.pop(); while(!Ause.empty())Ause.pop(); while(!Buse.empty())Buse.pop(); ans=0; scanf("%d%d%d",&n,&K,&L); for(int i=1;i<=n;i++)scanf("%d",&a[i]); for(int i=1;i<=n;i++)scanf("%d",&b[i]); for(int i=1;i<=n;i++){ H.push(mp(a[i]+b[i],i)); Ano.push(mp(a[i],i)); Bno.push(mp(b[i],i)); } Free=K-L; for(int i=1;i<=K;i++){ while(!Ano.empty()&&link[Ano.top().id])Ano.pop(); while(!Bno.empty()&&rink[Bno.top().id])Bno.pop(); if(Free){ int x=Ano.top().id; int y=Bno.top().id; link[x]=y;rink[y]=x; Ause.push(mp(b[x],x)); Buse.push(mp(a[y],y)); if(x!=y)Free--; Change(x);Change(y); ans+=a[x]+b[y]; continue; } //1. anone<->bnone //2. anone<->buse ause<->bnone //3. bnone<->ause buse<->anone int ans1=-1e9,ans2=-1e9,ans3=-1e9; while(!H.empty()&&(link[H.top().id]||rink[H.top().id]))H.pop(); if(!H.empty())ans1=H.top().val; while(!Buse.empty()&&(link[Buse.top().id]||rink[Buse.top().id]==Buse.top().id))Buse.pop(); if(!Buse.empty()&&!Bno.empty())ans2=Buse.top().val+Bno.top().val; while(!Ause.empty()&&(link[Ause.top().id]==Ause.top().id||rink[Ause.top().id]))Ause.pop(); if(!Ause.empty()&&!Ano.empty())ans3=Ause.top().val+Ano.top().val; if(ans1>=ans2&&ans1>=ans3){ int x=H.top().id; link[x]=rink[x]=x; } else if(ans2>=ans1&&ans2>=ans3){ int x=Buse.top().id,y=Bno.top().id,z=rink[x]; link[z]=y;rink[y]=z;Free+=(z==y); link[x]=rink[x]=x; Buse.push(mp(a[y],y)); Change(y);Change(z); } else{ int x=Ause.top().id,y=Ano.top().id,z=link[x]; link[y]=z;rink[z]=y;Free+=(z==y); link[x]=rink[x]=x; Ause.push(mp(b[y],y)); Change(y);Change(z); } ans+=max(max(ans1,ans2),ans3); } printf("%lld\n",ans); } return 0; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/15033230.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(58)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示