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\) 个人被选择至少一次的方案集合。
由容斥原理:
则:
Part 3
对每个科目分别考虑。
对于总共 \(cnt\) 种分数可能,\(gt\) 个人分数「\(\gt\) 曼巴」,\(le\) 个人分数「\(\le\) 曼巴」。
则枚举曼巴的分数 \(s\),方案数显然为:
记为 \(calc(cnt, gt, le)\)。
但该式复杂度与值域相关。
对于这种情况,可以考虑多项式或者离散化思想。
离散化
在离散化中,不同数据个数是关键要素。
考虑枚举 \(i\) 表示有 \(i\) 种不同的分数。令 \(f_i\) 为这种情况下的答案。
我们可以通过 \(calc(i, r - 1, n - r)\) 算出方案数,但这样只保证不同分数个数 \(\le i\) 而不是 \(= i\)。
考虑把 \(\lt i\) 的情况扣掉,即:
那么 \(\sum \limits _ i \binom u i f_i\) 即为当前科目的方案数。
\(Z\) 为所有科目方案数连乘。
多项式
应该可以吧,挖个坑等会填。
代码
#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);
}

浙公网安备 33010602011771号