1. 程式人生 > 其它 >【luogu AT5159】RGB Balls(貪心)

【luogu AT5159】RGB Balls(貪心)

給你一個 3*n 的字串,分別有 n 個 R,G,B 字元。 然後你可以把一個 R,G,B 組成一組,費用是它們的最長距離差。 然後你要把字串弄成 n 組,每個字元都被選到過,然後要他們的費用和最小。 問你有多少種弄的方案滿足費用和最小。

RGB Balls

題目連結:luogu AT5159

題目大意

給你一個 3*n 的字串,分別有 n 個 R,G,B 字元。
然後你可以把一個 R,G,B 組成一組,費用是它們的最長距離差。
然後你要把字串弄成 n 組,每個字元都被選到過,然後要他們的費用和最小。
問你有多少種弄的方案滿足費用和最小。

思路

我們首先考慮怎樣會有最小費用和。

那把最前和最後的貢獻分開,就是要前面的晚出現,後面的早出現。
那我們就能放到前面就放,這樣是最優的。

那接著考慮統計方案。
發現如果你要作為第三個,前面的兩個的出現順序並不重要。

那我們直接記錄六種狀態的當前個數(\(R,G,B,RG,RB,GB\)
那然後就能轉就轉,能三個就三個,每次答案乘可以轉過來的個數就可以了。

程式碼

#include<cstdio>
#include<iostream>
#include<algorithm>
#define ll long long
#define mo 998244353

using namespace std;

int n;
char s[300001];
ll ans, nm[6];//R G B RG RB GB 

int ck(int i) {
	if (s[i] == 'R') return 0;
	if (s[i] == 'G') return 1;
	return 2;
}

int to(int x, int y) {
	if (x > y) swap(x, y);
	if (x == 0 && y == 1) return 3;
	if (x == 0 && y == 2) return 4;
	return 5;
}

void cg(int x, int y) {
	if (nm[x]) nm[x]--, nm[y]++;
}

int main() {
	scanf("%d", &n);
	scanf("%s", s + 1);
	
	ans = 1;
	for (int i = 1; i <= n; i++) ans = ans * i % mo;
	for (int i = 1; i <= 3 * n; i++) {
		int now = ck(i);
		if (nm[5 - now]) ans = ans * nm[5 - now] % mo, nm[5 - now]--;
			else if (nm[(now + 1) % 3] || nm[(now + 2) % 3]) ans = ans * (nm[(now + 1) % 3] + nm[(now + 2) % 3]) % mo, cg((now + 1) % 3, to(now, (now + 1) % 3)), cg((now + 2) % 3, to(now, (now + 2) % 3));
				else nm[now]++;
	}
	
	printf("%lld", ans);
	
	return 0;
}