AtCoder Beginner Contest 227 E Swap

洛谷传送门

AtCoder 传送门

感觉是很裸的题。

考虑一个串 \(T\),它要交换多少次相邻字符才能等于 \(S\)。这是一个经典问题,设 \(p_i\)\(T_i\)\(T\) 的前缀 \([1, i]\) 中的出现次数,\(q_i\)\(T_i\)\(S\) 中第 \(p_i\) 次出现的位置,那么答案就是排列 \(q\) 的逆序对数。证明平凡故略。

由此也容易发现当 \(k > \frac{n(n - 1)}{2}\) 时能排列出所有能够排列出来的字符串。设字符 \(c\) 的出现次数为 \(cnt_c\),那么此时答案即为 \(\frac{(|S|!)}{cnt_{\text{A}}! cnt_{\text{B}}! cnt_{\text{C}!}}\)

对于 \(k \le \frac{n(n - 1)}{2}\) 的情况,考虑 dp,\(f_{i, j, k, p}\) 表示当前填了 \(i\)\(\text{A}\)\(j\)\(\text{B}\)\(k\)\(\text{C}\),用了 \(p\) 次交换的方案数。转移就考虑 \(T_{i + j + k}\) 填哪个字符即可,因为 \(i, j, k\) 都知道,所以此时 \(q_{1 \sim i + j + k}\) 能被算出来,所以 \(q_{i + j + k}\) 贡献的逆序对数也可以算出来。

时间复杂度 \(O(|S|^6)\),但是因为常数小所以跑得很快。

code
// Problem: E - Swap
// Contest: AtCoder - KEYENCE Programming Contest 2021 (AtCoder Beginner Contest 227)
// URL: https://atcoder.jp/contests/abc227/tasks/abc227_e
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef __int128 lll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 35;

int a[maxn], n, m;
ll f[maxn][maxn][maxn][500];
char s[maxn];
vector<int> vc[3];

void solve() {
	scanf("%s%d", s + 1, &m);
	n = strlen(s + 1);
	for (int i = 1; i <= n; ++i) {
		a[i] = (s[i] == 'K' ? 0 : (s[i] == 'E' ? 1 : 2));
		vc[a[i]].pb(i);
	}
	if (m >= n * (n - 1) / 2) {
		lll res = 1;
		for (int i = 1; i <= n; ++i) {
			res *= i;
		}
		for (int x = 0; x <= 2; ++x) {
			for (int i = 1; i <= (int)vc[x].size(); ++i) {
				res /= i;
			}
		}
		printf("%lld\n", (ll)res);
		return;
	}
	f[0][0][0][0] = 1;
	for (int i = 0; i <= (int)vc[0].size(); ++i) {
		for (int j = 0; j <= (int)vc[1].size(); ++j) {
			for (int k = 0; k <= (int)vc[2].size(); ++k) {
				for (int p = 0; p <= m; ++p) {
					if (!i && !j && !k) {
						continue;
					}
					if (i) {
						int c = p;
						for (int x = 0; x < i - 1; ++x) {
							c -= (vc[0][x] > vc[0][i - 1]);
						}
						for (int x = 0; x < j; ++x) {
							c -= (vc[1][x] > vc[0][i - 1]);
						}
						for (int x = 0; x < k; ++x) {
							c -= (vc[2][x] > vc[0][i - 1]);
						}
						if (c >= 0) {
							f[i][j][k][p] += f[i - 1][j][k][c];
						}
					}
					if (j) {
						int c = p;
						for (int x = 0; x < i; ++x) {
							c -= (vc[0][x] > vc[1][j - 1]);
						}
						for (int x = 0; x < j - 1; ++x) {
							c -= (vc[1][x] > vc[1][j - 1]);
						}
						for (int x = 0; x < k; ++x) {
							c -= (vc[2][x] > vc[1][j - 1]);
						}
						if (c >= 0) {
							f[i][j][k][p] += f[i][j - 1][k][c];
						}
					}
					if (k) {
						int c = p;
						for (int x = 0; x < i; ++x) {
							c -= (vc[0][x] > vc[2][k - 1]);
						}
						for (int x = 0; x < j; ++x) {
							c -= (vc[1][x] > vc[2][k - 1]);
						}
						for (int x = 0; x < k - 1; ++x) {
							c -= (vc[2][x] > vc[2][k - 1]);
						}
						if (c >= 0) {
							f[i][j][k][p] += f[i][j][k - 1][c];
						}
					}
				}
			}
		}
	}
	ll ans = 0;
	for (int i = 0; i <= m; ++i) {
		ans += f[(int)vc[0].size()][(int)vc[1].size()][(int)vc[2].size()][i];
	}
	printf("%lld\n", ans);
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

posted @ 2023-06-15 14:18  zltzlt  阅读(33)  评论(0编辑  收藏  举报