zoukankan      html  css  js  c++  java
  • [NOI2019]序列(模拟费用流)

    题意:

    有两个长度为n的序列,要求从每个序列中选k个,并且满足至少有l个位置都被选,问总和最大是多少。
    (1leq lleq kleq nleq 2*10^5)
    首先,记录当前考虑到的位置i,第一个选的数量a, 第二个选的数量b,都被选的数量c,可以做到(O(n^4))
    卡常后能过(nleq 150),有40分。
    考虑正解:首先,看到这个范围,可以认为正解一定是贪心。
    先看下(nleq 2000),这个是网络流的范围。我们可以先建出费用流,然后再变为模拟费用流,即贪心。
    从源点向第一个序列连边,第二个序列向汇点连边。
    然后,我们发现至少有l个位置都被选不太容易表示,因为左面向对应的右面连的边是分开的,无法放到一起考虑下限。
    换一种思路:至少有l个位置都被选,就是剩的位置不超过(k-l)
    所以,我们可以这样建图:
    1、从源点向第一个序列连边,第二个序列向汇点连边。
    2、对应位置连流量为1的边。
    3、第一个序列都向点a连边,点b向第二个序列连边。a到b连容量(k-l)的边。
    求出流量为k的最大费用流即可。这样据说有64分。
    考虑模拟费用流优化:
    首先,记录s表示a到b的剩余流量。
    1、若(s>0),则可以通过a到b的边增广,即从两个序列中各选一个最大数,并把s减去1。

    2、可以直接走左右的对应连边增广,即选一个左右相加最大的位置。
    3、可以走(S->x->x'->b->y'->T),走(x'->b)的前提是x在第二个序列中被选。
    即在第一个序列中选一个第二个序列中选的位置,再选一个第二个序列中被选的作为y。
    4、与3对称,即在第二个序列中选一个第一个序列中选的位置,再选一个第一个序列中被选的。
    因为是最长路增广,所以在2,3,4中选最大的。以上操作可以用5个优先队列完成。

    然而,我们发现过不去样例。
    其实,算法是对的,但我们少了一些情况(特判):
    首先,还可以走(S->x->x'->b->a->y->y'->T)。(情况5)
    就是在第二个序列中选一个第一个序列中选的位置,再在第一个序列中选一个第二个序列中选的位置,
    因为退了a到b的流,要把s加1。

    在1中,若选的两个位置相同,则不用减s。若选的位置在另一序列中已被选,则为情况3或4,不用减s。
    若选的位置在另一序列中已被选,则为情况5,把s加1。
    在3,4中,可能会转移到情况5,此时要把s加1。

    加上这些后就能过了,实现时要注意细节。

    代码:

    #include <stdio.h> 
    #include <queue> 
    using namespace std;
    struct SJd {
    	int z,	i;
    	SJd() {}
    	SJd(int Z, int I) {
    		z = Z;i = I;
    	}
    };
    bool operator < (const SJd a, const SJd b) {
    	return a.z < b.z;
    }
    #define prio priority_queue < SJd > 
    #define ll long long 
    int sa[200010],sb[200010],ba[200010],bb[200010];
    prio pa,pb,pc,pd,pe,em;
    inline int read() {
    	char ch;
    	while ((ch = getchar()) < '0' || ch > '9');
    	int rt = ch - '0';
    	while ((ch = getchar()) >= '0' && ch <= '9') rt = (rt << 3) + (rt << 1) + ch - '0';
    	return rt;
    }
    int main() {
    	int T;
    	scanf("%d", &T);
    	while (T--) {
    		pa = pb = pc = pd = pe = em;
    		int n,k,l;
    		n = read();
    		k = read();
    		l = read();
    		for (int i = 0; i < n; i++) sa[i] = read();
    		for (int i = 0; i < n; i++) sb[i] = read();
    		for (int i = 0; i < n; i++) {
    			pa.push(SJd(sa[i], i));
    			pb.push(SJd(sb[i], i));
    			pe.push(SJd(sa[i] + sb[i], i));
    			ba[i] = bb[i] = false;
    		}
    		int sy = k - l;
    		ll ans = 0;
    		for (int i = 0; i < k; i++) {
    			SJd ra,rb,rc,rd,re;
    			while (!pa.empty()) {
    				ra = pa.top();
    				if (!ba[ra.i]) break;
    				pa.pop();
    			}
    			while (!pb.empty()) {
    				rb = pb.top();
    				if (!bb[rb.i]) break;
    				pb.pop();
    			}
    			if (sy > 0) {
    				if (bb[ra.i]) sy += 1;
    				if (ba[rb.i]) sy += 1;
    				if (ra.i == rb.i) sy += 1;
    				ba[ra.i] = bb[rb.i] = true;
    				pc.push(SJd(sa[rb.i], rb.i));
    				pd.push(SJd(sb[ra.i], ra.i));
    				sy -= 1;
    				ans += ra.z + rb.z;
    				continue;
    			}
    			while (!pc.empty()) {
    				rc = pc.top();
    				if (!ba[rc.i]) break;
    				pc.pop();
    			}
    			while (!pd.empty()) {
    				rd = pd.top();
    				if (!bb[rd.i]) break;
    				pd.pop();
    			}
    			while (!pe.empty()) {
    				re = pe.top();
    				if (!ba[re.i] && !bb[re.i]) break;
    				pe.pop();
    			}
    			int ma = -1,
    			lx = -1;
    			if (!pc.empty() && !pb.empty() && rc.z + rb.z > ma) ma = rc.z + rb.z,lx = 1;
    			if (!pd.empty() && !pa.empty() && rd.z + ra.z > ma) ma = rd.z + ra.z,lx = 2;
    			if (!pe.empty() && re.z > ma) ma = re.z,lx = 3;
    			ans += ma;
    			if (lx == 1) {
    				if (ba[rb.i]) sy += 1;
    				ba[rc.i] = bb[rb.i] = true;
    				pc.pop();
    				pb.pop();
    				pc.push(SJd(sa[rb.i], rb.i));
    			} else if (lx == 2) {
    				if (bb[ra.i]) sy += 1;
    				ba[ra.i] = bb[rd.i] = true;
    				pa.pop();
    				pd.pop();
    				pd.push(SJd(sb[ra.i], ra.i));
    			} else if (lx == 3) {
    				ba[re.i] = bb[re.i] = true;
    				pe.pop();
    			}
    		}
    		printf("%lld
    ", ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    JS中的函数声明存在的“先使用,后定义”
    JS原型与原型链终极详解
    Angular--页面间切换及传值的四种方法
    Angularjs Controller 间通信机制
    angularjs 中使用 service 在controller 之间 share 对象和数据
    CSS中!important的使用
    angularjs checkbox 框的操作
    纯CSS气泡框实现方法探究
    AngularJS 实现页面滚动到底自动加载数据的功能
    AngularJS的Filter用法详解
  • 原文地址:https://www.cnblogs.com/lnzwz/p/11409572.html
Copyright © 2011-2022 走看看