zoukankan      html  css  js  c++  java
  • P5470-[NOI2019]序列【模拟费用流】

    正题

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


    题目大意

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

    求最大化两个子序列的和。
    (Tleq 10,1leq nleq 2 imes 10^5, sum nleq 10^6)


    解题思路

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

    然后开设一条流量为(K-L)的边供所有(a_i)连接所有(b_i)表示这些可以随便匹配,然后剩下的都是(a_i)连接(b_i)就好了。

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

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

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

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

    但是这样会发现还是有问题,比如有时我们会出现(a_x<->b_y,b_x<->a_y)的情况,此时相当与从(a_{x/y}<->b_{x/y})增广。

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

    时间复杂度(O(nlog 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
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    curl上传图片文件
    手工制作简单后台模板
    首页自动生成静态化html
    svn-多个项目版本库和自动同步更新post-commit
    Snoopy+phpquery采集demo
    phpstorm使用手册
    mac系统使用帮助
    upupw一键绿色免安装环境包
    去掉文件夹的.svn文件
    centos安装svn服务器
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/15033230.html
Copyright © 2011-2022 走看看