LOJ 3158: 「NOI2019」序列


題目傳送門:LOJ #3158

題意簡述:

給定兩個長度為 \(n\) 的正整數序列 \(a,b\),要求在每個序列中都選中 \(K\) 個下標,並且要保證同時在兩個序列中都被選中的下標至少有 \(L\) 個,使得選中的下標對應的數的總和最大。

題解:

題目相當於要求在兩個序列中選出 \(K\) 對數,不妨一對一對地選。

有個結論是說,上一步的最優決策一定不會再反悔,就是已經選的不會再撤銷。

然后做完了,用堆維護一些東西,精細實現就好了。

下面是代碼,復雜度 \(\mathcal{O}\left(\sum n\log n\right)\)

#include <cstdio>
#include <algorithm>
#include <queue>
#include <cctype>

#define fi first
#define se second
#define mp std::make_pair
typedef long long LL;
typedef std::pair<int, int> pii;
typedef std::priority_queue<pii> PQ;
const int MN = 200005;

inline void read(int &x) {
	x = 0; static char ch;
	while (!isdigit(ch = getchar())) ;
	while (x = x * 10 + (ch & 15), isdigit(ch = getchar()));
}
inline void write(LL x) {
	static char st[15];
	static char *t = st;
	while (x) *t++ = (x % 10) | 48, x /= 10;
	while (t != st) putchar(*--t);
	putchar('\n');
}

int N, K, L;
int A[MN], B[MN];
bool vA[MN], vB[MN];
PQ A0, B0, A1, B1, C;
LL Ans;

int main() {
	freopen("sequence.in", "r", stdin);
	freopen("sequence.out", "w", stdout);
	int T; scanf("%d", &T);
	while (T--) {
		Ans = 0;
		while (!A0.empty()) A0.pop();
		while (!B0.empty()) B0.pop();
		while (!A1.empty()) A1.pop();
		while (!B1.empty()) B1.pop();
		while (!C.empty()) C.pop();
		read(N), read(K), read(L);
		for (int i = 1; i <= N; ++i) read(A[i]);
		for (int i = 1; i <= N; ++i) read(B[i]);
		for (int i = 1; i <= N; ++i) {
			vA[i] = vB[i] = 0;
			A0.push(mp(A[i], i));
			B0.push(mp(B[i], i));
			C.push(mp(A[i] + B[i], i));
		}
		int left = K - L;
		for (int cnt = 1; cnt <= K; ++cnt) {
			while (vA[A0.top().se]) A0.pop();
			while (vB[B0.top().se]) B0.pop();
			if (left) {
				pii a = A0.top(), b = B0.top();
				A0.pop(), B0.pop();
				Ans += a.fi + b.fi;
				int ap = a.se, bp = b.se;
				vA[ap] = vB[bp] = 1;
				if (ap == bp) C.pop();
				else {
					--left;
					if (vB[ap]) ++left, A1.pop();
					else B1.push(mp(B[ap], ap));
					if (vA[bp]) ++left, B1.pop();
					else A1.push(mp(A[bp], bp));
				}
			}
			else {
				pii c0 = C.empty() ? mp(0, 0) : C.top();
				while (vA[c0.se] || vB[c0.se])
					C.pop(), c0 = C.empty() ? mp(0, 0) : C.top();
				pii a0 = A0.top(), b0 = B0.top();
				pii a1 = A1.empty() ? mp(-b0.fi, 0) : A1.top();
				pii b1 = B1.empty() ? mp(-a0.fi, 0) : B1.top();
				int a = a1.fi + b0.fi;
				int b = b1.fi + a0.fi;
				if (c0.fi >= a && c0.fi >= b)
					Ans += c0.fi, vA[c0.se] = vB[c0.se] = 1;
				else if (a >= b) {
					Ans += a;
					A1.pop(), B0.pop(), vA[a1.se] = vB[b0.se] = 1;
					if (b1.se == b0.se) B1.pop(), ++left;
					else A1.push(mp(A[b0.se], b0.se));
				}
				else {
					Ans += b;
					B1.pop(), A0.pop(), vB[b1.se] = vA[a0.se] = 1;
					if (a1.se == a0.se) A1.pop(), ++left;
					else B1.push(mp(B[a0.se], a0.se));
				}
			}
		} write(Ans);
	}
	return 0;
}

證明?不要問我證明啊!其實仔細分析一下應該是能證出來的,再不行就先套個費用流模型。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM