• @loj



    @description@

    给定两个长度为 n 的正整数序列 {ai} 与 {bi},序列的下标为 1,2,…,n。
    现在你需要分别对两个序列各指定恰好 K 个下标,要求至少有 L 个下标在两个序列中都被指定,使得这 2K 个下标在序列中对应的元素的总和最大。

    形式化地说,你需要确定两个长度为 K 的序列 {ci},{di},其中 1≤c1<c2<…<cK≤n,1≤d1<d2<…<dK≤n,并要求 |{c1,c2,…,cK}∩{d1,d2,…,dK}|≥L。目标是最大化:

    [sum_{i=1}^{K}(a_{c_i} + b_{d_i}) ]

    @solution@

    首先 O(n^3) 的 dp 人人都会。然后据说可以优化出来

    考虑换思路,使用费用流建模:
    将点 i 拆成 i -> i',费用 0 容量 1;
    然后连 s1 -> i 费用 ai 容量 1,连 i' -> t1 费用 bi 容量 1。
    再连 s2 -> i' 费用 0 容量 1,连 i -> t2 费用 0 容量 1。
    最后连 s -> s1 费用 0 容量 K,连 s -> s2 费用 0 容量 K - L;连 t1 -> t 费用 0 容量 K,连 t2 -> t 费用 0 容量 K - L。
    (好像跟网上的一般建图方法不大一样来着。。。不过无所谓,一样可以优化)

    这样跑最小费用流(或者最小费用最大流也可)就可以有 40 分的好成绩(靠,我当时为什么没想到)

    考虑优化。注意使用连续最短路算法时,我们总会先选择前 K 个 ai + bi 最大的 i,并发送流量 s -> s1 -> i -> i' -> t1 -> t。
    然后我们会跑 K - L 次最短路。每次最短路总会是以下几种效果之一:
    1)ai + bi -> ai + bj。
    2)ai + bi -> aj + bi。
    3)ai + bi + aj + bj -> ai + bj + ak + bk。
    4)ai + bi -> aj + bk。

    可以自己手算一下边界情况(如 K - L = 1)验证一下,或者手画一个网络流跑一跑。

    然后就可以用可删除堆 O(nlog n) 乱做。

    @accepted code@

    #include <queue>
    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    typedef long long ll;
    typedef pair<int, int> pii;
    typedef pair<ll, int> pli;
    #define fi first
    #define se second
    #define mp make_pair
    
    const int MAXN = 200000;
    const ll INF = (1LL << 60);
    
    int tag[MAXN + 5];
    struct heap{
    	int type; priority_queue<pli>q; heap() {}
    	heap(int _t) : type(_t) {}
    	void maintain() {
    		while( !q.empty() && tag[q.top().se] != type && (type == 3 || tag[q.top().se] != 0) )
    			q.pop();
    	}
    	bool empty() {maintain(); return q.empty();}
    	pli top() {maintain(); return empty() ? make_pair(-INF, -1) : q.top();}
    	void push(pli x) {q.push(x); maintain();}
    	void pop() {q.pop(); maintain();}
    };
    
    int read() {
    	int x = 0, ch = getchar();
    	while( ch > '9' || ch < '0' ) ch = getchar();
    	while( '0' <= ch && ch <= '9' ) x = 10*x + ch - '0', ch = getchar();
    	return x;
    }
    
    pii a[MAXN + 5];
    bool cmp_ab(const pii &x, const pii &y) {
    	return x.fi + x.se > y.fi + y.se;
    }
    void solve() {
    	int n = read(), K = read(), L = read();
    	for(int i=1;i<=n;i++) a[i].fi = read();
    	for(int i=1;i<=n;i++) a[i].se = read();
    	
    	ll ans = 0; heap hn1(3), hn2(3), hn3(3), hx1(0), hx2(1), hx3(2);
    	sort(a + 1, a + n + 1, cmp_ab);
    	for(int i=1;i<=K;i++) {
    		ans += a[i].fi + a[i].se;
    		
    		tag[i] = 3;
    		hn1.push(mp(-(a[i].fi + a[i].se), i));
    		hn2.push(mp(-a[i].fi, i)), hn3.push(mp(-a[i].se, i));
    	}
    	for(int i=K+1;i<=n;i++) {
    		tag[i] = 0;
    		hx1.push(mp(a[i].fi + a[i].se, i));
    		hx2.push(mp(a[i].fi, i)), hx3.push(mp(a[i].se, i));
    	}
    	for(int i=1;i<=K-L;i++) {
    		pli mnab = hn1.top(), mna = hn2.top(), mnb = hn3.top();
    		pli mxab = hx1.top(), mxa = hx2.top(), mxb = hx3.top();
    		
    		ll da = mxa.fi + mna.fi, db = mxb.fi + mnb.fi;
    		ll dc = mxab.fi + mna.fi + mnb.fi, dd = mxa.fi + mxb.fi + mnab.fi;
    		if( dd > dc && dd > db && dd > da && dd > 0 ) {
    			ans += dd;
    			tag[mxb.se] = 1, hx2.push(mp(a[mxb.se].fi, mxb.se));
    			tag[mxa.se] = 2, hx3.push(mp(a[mxa.se].se, mxa.se));
    			tag[mnab.se] = 0;
    			hx1.push(mp(a[mnab.se].fi + a[mnab.se].se, mnab.se));
    			hx2.push(mp(a[mnab.se].fi, mnab.se)), hx3.push(mp(a[mnab.se].se, mnab.se));
    		} else if( dc > da && dc > db && dc > 0 ) {
    			ans += dc;
    			tag[mna.se] = 1, hx2.push(mp(a[mna.se].fi, mna.se));
    			tag[mnb.se] = 2, hx3.push(mp(a[mnb.se].se, mxb.se));
    			tag[mxab.se] = 3;
    			hn1.push(mp(-(a[mxab.se].fi + a[mxab.se].se), mxab.se));
    			hn2.push(mp(-a[mxab.se].fi, mxab.se)), hn3.push(mp(-a[mxab.se].se, mxab.se));
    		} else if( da > db && da > 0 ) {
    			ans += da;
    			tag[mna.se] = 1, hx2.push(mp(a[mna.se].fi, mna.se));
    			tag[mxa.se] = 2, hx3.push(mp(a[mxa.se].se, mxa.se));
    		} else if( db > 0 ) {
    			ans += db;
    			tag[mxb.se] = 1, hx2.push(mp(a[mxb.se].fi, mxb.se));
    			tag[mnb.se] = 2, hx3.push(mp(a[mnb.se].se, mnb.se));
    		} else break;
    	}
    	printf("%lld
    ", ans);
    }
    
    int main() {
    	freopen("sequence.in", "r", stdin);
    	freopen("sequence.out", "w", stdout);
    	
    	int T; scanf("%d", &T);
    	while( T-- ) solve();
    }
    

    @details@

    去年写了一个丢人的假贪心,还没发现连大样例都过不了 = =。

    关于模拟费用流,除了直接优化连续最短路算法过程外,还有就是 WC2019 上讲的一类老鼠进洞问题(每次加边,动态维护费用流)。

    这种用费用流的思想分析贪心问题(或者用贪心优化费用流过程)的好处是,它原本构造性的结论可以与费用流中众所周知的性质转化,更方便思考。

  • 相关阅读:
    java面向对象3
    java面向对象2
    java面向对象1
    java基础5
    java基础4
    java基础3
    递归之汉诺塔问题
    自定义 strcpy函数
    自定义strcmp函数
    自定义strcat函数
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/13031274.html
Copyright © 2020-2023  润新知