Loading

P3270 [JLOI2016] 成绩比较 组合数学解法

P3270 [JLOI2016] 成绩比较

题意

\(n\) 个人,\(m\) 个科目,每个人科目 \(i\) 的分数是 \([1, u_i]\) 中的整数。

其中一个人是曼巴。现在已知恰好 \(k\) 个人的所有科目分数都 \(\le\) 曼巴的。(被曼巴肘飞)

给出曼巴在每个科目的排名 \(r_i\),这表示在科目 \(i\)\(r_i - 1\) 个人分数 \(\gt\) 曼巴,\(n - r_i\) 个人分数 \(\lt\) 曼巴。

求所有人分数的可能方案数。

思路

将问题分为三个部分:

  • 选出 \(k\) 个人被曼巴肘飞。记答案为 \(X = \binom {n - 1} k\)

  • 确定出剩余 \(n - 1 - k\) 个人在每个科目的分数是「\(\gt\) 曼巴」还是「\(\le\) 曼巴」。记答案为 \(Y\)

  • 确认出所有人的分数。记答案为 \(Z\)

Part 2

对于科目 \(j\),选出「分数 \(\gt\) 曼巴」方案数为 \(\binom {n - 1 - k} {r_j - 1}\)

但我们有另外一个隐藏条件,即这 \(n - 1 - k\) 个人是一定要被选择至少一次。

考虑容斥,令 \(S_i\) 为第 \(i\) 个人被选择至少一次的方案集合。

由容斥原理:

\[\begin{align*} | \bigcap \limits _ i S_i | &= |U| - | \bigcup \limits _ i \overline{S_i} | \\ &= |U| - \sum \limits _ {T \subseteq S, T \ne \varnothing} (-1) ^ {|T| - 1} \times | \bigcap \limits _ {i \in T} \overline{S_i}|\\ &= |U| + \sum \limits _ {T \subseteq S, T \ne \varnothing} (-1) ^ {|T|} \times | \bigcap \limits _ {i \in T} \overline{S_i}|\\ &= \sum \limits _ {T \subseteq S} (-1) ^ {|T|} \times | \bigcap \limits _ {i \in T} \overline{S_i}| \end{align*} \]

则:

\[Y = \sum _ {i = 0} ^ {n - 1 - k} (-1) ^ i \binom {n - 1 - k} i \prod _ {j = 1} ^ m \binom {n - 1 - k - i} {r_j - 1} \]

Part 3

对每个科目分别考虑。

对于总共 \(cnt\) 种分数可能,\(gt\) 个人分数「\(\gt\) 曼巴」,\(le\) 个人分数「\(\le\) 曼巴」。
则枚举曼巴的分数 \(s\),方案数显然为:

\[\sum _ {s = 1} ^ {cnt} (cnt - s) ^ {gt} s ^ {le} \]

记为 \(calc(cnt, gt, le)\)

但该式复杂度与值域相关。

对于这种情况,可以考虑多项式或者离散化思想。

离散化

在离散化中,不同数据个数是关键要素。
考虑枚举 \(i\) 表示有 \(i\) 种不同的分数。令 \(f_i\) 为这种情况下的答案。

我们可以通过 \(calc(i, r - 1, n - r)\) 算出方案数,但这样只保证不同分数个数 \(\le i\) 而不是 \(= i\)
考虑把 \(\lt i\) 的情况扣掉,即:

\[f_i = calc(i, r - 1, n - r) - \sum _ {j = 1} ^ {i - 1} f_j \binom i k \]

那么 \(\sum \limits _ i \binom u i f_i\) 即为当前科目的方案数。

\(Z\) 为所有科目方案数连乘。

多项式

应该可以吧,挖个坑等会填。

代码

record

#define int ll

const int N = 105;
const int mod = 1e9 + 7;

int n, m, k;
int u[N], r[N];

int X, Y, Z;

int qpow(int a, int b){
	int res = 1;
	while(b){
		if(b & 1) res = res * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return res;
}

int fac[N], inv_fac[N], inv[N];
void init(){
	fac[0] = 1;
	rep(i, 1, n){
		fac[i] = fac[i - 1] * i % mod;
	}
	inv_fac[n] = qpow(fac[n], mod - 2);
	per(i, n - 1, 0){
		inv_fac[i] = inv_fac[i + 1] * (i + 1) % mod;
	}
	rep(i, 1, n){
		inv[i] = qpow(i, mod - 2);
	}
}
int C(int a, int b){
	return fac[a] * inv_fac[a - b] % mod * inv_fac[b] % mod;
}

int calc(int cnt, int gt, int le){
	int res = 0;
	rep(s, 1, cnt){
		int tmp = qpow(cnt - s, gt) * qpow(s, le) % mod;
		res = (res + tmp) % mod;
	}
	return res;
}
int f[N];

void solve_test_case(){
	n = read(), m = read(), k = read();
	rep(i, 1, m) u[i] = read();
	rep(i, 1, m) r[i] = read();
	
	init();
	
	X = C(n - 1, k);
	
	rep(i, 0, n - 1 - k){
		int res = C(n - 1 - k, i);
		rep(j, 1, m){
			res = res * C(n - 1 - k - i, r[j] - 1) % mod;
		}
		if(i % 2 == 0) Y = (Y + res) % mod;
		else Y = (Y - res + mod) % mod;
	}
	
	Z = 1;
	rep(k, 1, m){
		int res = 0;
		int com = 1;
		rep(i, 1, n){
			int tmp = calc(i, r[k] - 1, n - r[k]);
			rep(j, 1, i - 1){
				tmp = (tmp - C(i, j) * f[j] % mod + mod) % mod;
			}
			f[i] = tmp;
			com = com * (u[k] - i + 1) % mod * inv[i] % mod;
			res = (res + f[i] * com % mod) % mod;
		}
		Z = Z * res % mod;
	}
	
	write(X * Y % mod * Z % mod);
}

posted @ 2026-01-13 17:40  lajishift  阅读(3)  评论(0)    收藏  举报