[CSA] Number Elimination

Solution

要求最小代价的方案数,所以我们显然可以直接把这些元素从小到大排序

我们令\(f_i\)表示消去\(i\)个一样的数字的方案数,不难得出\(f_i = \frac {i \cdot (i - 1)} {2} f_{i - 1}\)

假设当前有\(i\)个数字,我们可以任选两个数字把编号小的消去,所以方案数为\(i \choose 2\)

然后就转化成\(i - 1\)个数字的局面了

把值相等的数字分为一组,设\(len_i\)表示第\(i\)组数字的个数,设\(sum_i\)表示前\(i\)数字的总长度,\(dp_i\)表示消去前\(i\)组的方案数,则有:

\[dp_i = dp_{i - 1} \cdot f_{len_i} \cdot \sum _{j = 0} ^ {j = len_i - 1} {{sum_{i - 1} - 1 + j} \choose j} \cdot (len_i - j) \]

前面乘了一个\(f_{len_i}\)表示已经考虑了本组的内部消除顺序

组合数表示删掉前面的所有数(除上组最后一个)以及当前组的任意\(j\)个数的先后顺序方案数

最后后面那个式子表示上组最后一个可以被当前组剩下未删的任意一个数消去

Code

#include <bits/stdc++.h>

using namespace std;

#define fst first
#define snd second
#define squ(x) ((LL)(x) * (x))
#define debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long LL;
typedef pair<int, int> pii;

inline int read() {
	int sum = 0, fg = 1; char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') fg = -1;
	for (; isdigit(c); c = getchar()) sum = (sum << 3) + (sum << 1) + (c ^ 0x30);
	return fg * sum;
}

const int maxn = 1e5 + 10;
const int mod = 1e9 + 7;

inline int add(int x, int y) { return (x += y) < mod ? x : x - mod; }
inline int mul(LL x, int y) { return (LL)x * y % mod; }

int Pow(int x, int y) {
	int res = 1;
	while (y) {
		if (y & 1) res = mul(res, x);
		x = mul(x, x); y >>= 1;
	}
	return res;
}

int n, N, a[maxn], len[maxn], sum[maxn], dp[maxn], f[maxn];

int fac[maxn], ifac[maxn];

inline int C(int _n, int _m) { return mul(fac[_n], mul(ifac[_n - _m], ifac[_m])); }

void init() {
	fac[0] = 1;
	for (int i = 1; i <= n; i++) fac[i] = mul(fac[i - 1], i);
	ifac[n] = Pow(fac[n], mod - 2);
	for (int i = n - 1; ~i; i--) ifac[i] = mul(ifac[i + 1], i + 1);
}

int main() {
#ifdef xunzhen
	freopen("out.in", "r", stdin);
	freopen("out.out", "w", stdout);
#endif

	n = read(); init();
	for (int i = 1; i <= n; i++) a[i] = read();

	sort(a + 1, a + n + 1);
	int pos = 1;
	for (int i = 2; i <= n + 1; i++)
		if (a[i] != a[i - 1]) len[++N] = i - pos, sum[N] = sum[N - 1] + len[N], pos = i;

	f[1] = 1;
	for (int i = 2; i <= n; i++) f[i] = mul(f[i - 1], mul(mul(i, i - 1), ifac[2]));

	dp[1] = f[len[1]];
	for (int i = 2; i <= N; i++) {
		int Sum = 0;
		for (int j = 0; j < len[i]; j++)
			Sum = add(Sum, mul(C(sum[i - 1] - 1 + j, j), len[i] - j));
		dp[i] = mul(mul(dp[i - 1], f[len[i]]), Sum);
	}

	printf("%d\n", dp[N]);

	return 0;
}

Summary

这个DP的思维好神啊,完全想不到 考试的时候部分分都打挂了

posted @ 2018-10-24 17:20  xunzhen  阅读(205)  评论(0编辑  收藏  举报