P5470-[NOI2019]序列【模擬費用流】
阿新 • • 發佈:2021-07-20
正題
題目連結:https://www.luogu.com.cn/problem/P5470
題目大意
兩個長度為\(n\)的序列\(a,b\),求出它們兩個長度為\(K\)的子序列,且這兩個子序列至少有\(L\)個位置下標相等。
求最大化兩個子序列的和。
\(T\leq 10,1\leq n\leq 2\times 10^5, \sum n\leq 10^6\)
解題思路
先考慮一個簡單的費用流做法,我們對於\(a_i\)建在左邊,\(b_i\)建在右邊。
然後開設一條流量為\(K-L\)的邊供所有\(a_i\)連線所有\(b_i\)表示這些可以隨便匹配,然後剩下的都是\(a_i\)連線\(b_i\)就好了。
但是費用流顯然會\(T\),所以考慮一下怎麼模擬這個過程。
記流\(K-L\)的為自由流,其他的為限制流,我們肯定會把自由流先流滿,模擬的過程就是每次選最大的兩個匹配,當然如果這兩個恰好是一起的就流限制流就好了。
自由流流滿之後有三種操作
- 正常流限制流,選取最大的一個兩邊都未匹配的流過去
- 尋找增廣路,我們肯定是會退流一條自由流匹配的,此時我們需要找到一個限制流。這裡拿\(a\)舉例,也就是我們需要找到匹配過的\(a_i\)且\(b_i\)未匹配,此時我們增廣\(a_i->b_i\),那麼之前與\(a_i\)匹配的就需要找一個最大的\(b_i\)匹配。\(b\)同理。
但是這樣會發現還是有問題,比如有時我們會出現\(a_x<->b_y,b_x<->a_y\)
每次匹配完之後判斷一下這種情況就好了。開5個堆維護即可
時間複雜度\(O(n\log n)\)
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<->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; }