日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2023/12/3 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 P5470-[NOI2019]序列【模拟费用流】 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

正題

題目鏈接: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^6T10,1n2×105,n106


解題思路

先考慮一個簡單的費用流做法,我們對于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(nlog?n)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; }

    總結

    以上是生活随笔為你收集整理的P5470-[NOI2019]序列【模拟费用流】的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。