正题
题目链接:https://www.luogu.com.cn/problem/P5470
题目大意
两个长度为nnn的序列a,ba,ba,b,求出它们两个长度为KKK的子序列,且这两个子序列至少有LLL个位置下标相等。
求最大化两个子序列的和。
T≤10,1≤n≤2×105,∑n≤106T\leq 10,1\leq n\leq 2\times 10^5, \sum n\leq 10^6T≤10,1≤n≤2×105,∑n≤106
解题思路
先考虑一个简单的费用流做法,我们对于aia_iai建在左边,bib_ibi建在右边。
然后开设一条流量为K−LK-LK−L的边供所有aia_iai连接所有bib_ibi表示这些可以随便匹配,然后剩下的都是aia_iai连接bib_ibi就好了。
但是费用流显然会TTT,所以考虑一下怎么模拟这个过程。
记流K−LK-LK−L的为自由流,其他的为限制流,我们肯定会把自由流先流满,模拟的过程就是每次选最大的两个匹配,当然如果这两个恰好是一起的就流限制流就好了。
自由流流满之后有三种操作
- 正常流限制流,选取最大的一个两边都未匹配的流过去
- 寻找增广路,我们肯定是会退流一条自由流匹配的,此时我们需要找到一个限制流。这里拿aaa举例,也就是我们需要找到匹配过的aia_iai且bib_ibi未匹配,此时我们增广ai−>bia_i->b_iai−>bi,那么之前与aia_iai匹配的就需要找一个最大的bib_ibi匹配。bbb同理。
但是这样会发现还是有问题,比如有时我们会出现ax<−>by,bx<−>aya_x<->b_y,b_x<->a_yax<−>by,bx<−>ay的情况,此时相当与从ax/y<−>bx/ya_{x/y}<->b_{x/y}ax/y<−>bx/y增广。
每次匹配完之后判断一下这种情况就好了。开5个堆维护即可
时间复杂度O(nlogn)O(n\log n)O(nlogn)
code
#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<->anoneint 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;
}