[JLOI 2016]成绩比较

时间:2022-12-12 20:57:09

Description

题库链接( \(\text{bzoj}\) 不知道为什么过不了啊... \(\text{luogu loj}\) 都能过...就给 \(\text{luogu}\) 的链接了...)

共有 \(n\) 位同学, \(M\) 门必修课。一位同学在必修课上可以获得的分数是 \(1\) 到 \(U_i\) 中的一个整数。

如果在每门课上 \(A\) 获得的成绩均小于等于 \(B\) 获得的成绩,则称 \(A\) 被 \(B\) 碾压。在 \(B\) 神的说法中,共有 \(K\) 位同学被他碾压(不包括他自己)。

\(B\) 神在第 \(i\) 门必修课中排名为 \(R_i\) 。这里的排名是指:如果 \(B\) 神某门课的排名为 \(R\) ,则表示有且仅有 \(R-1\) 位同学这门课的分数大于 \(B\) 神的分数,有且仅有 \(N-R\) 位同学这门课的分数小于等于 \(B\) 神(不包括他自己)。

我们需要求出全系所有同学每门必修课得分的情况数,使其既能满足B神的说法,也能符合排名。这里两种情况不同当且仅当有任意一位同学在任意一门课上获得的分数不同。

计算出情况数模 \(10^9+7\) 的。

\(N\leq 100,M\leq 100,Ui\leq 10^9\)

Solution

由于只有 \(k\) 名同学被碾压,故要保证其余的 \(n-1-k\) 名同学至少有一门分数要高于他。

我们有 \(n-1\choose k\) 种方案选出被碾压的 \(k\) 个人,对于剩下的 \(n-1-k\) 个人,我们记 \(f_x\) 为至多有 \(x\) 个人满足至少一门分数高于他,那么 \(f_x=\prod\limits_{i=1}^m{x\choose R_i-1}\) 。再设 \(g_x\) 为恰有 \(x\) 个人满足至少一门分数高于他,显然我们要求的是 \(g_{n-k-1}\) 。

容易得到

\[f_x=\sum_{i=1}^x {x\choose i}g_i\]

由二项式反演,得到

\[g_x=\sum_{i=1}^x (-1)^{x-i}{x\choose i}f_i\]

那么

\[g_{n-k-1}=\sum_{i=1}^{n-k-1}(-1)^{n-k-1-i}{n-k-1\choose i}\prod_{j=1}^m{i\choose R_j-1}\]

不过这只考虑了合法的安排相对分数高低情况,还没有考虑具体分数的关系。

不妨记 \(S\) 为一种人员安排情况下不同的分数安排方法。

我们枚举他的分数得到

\[\begin{aligned}S&=\prod_{i=1}^m\sum_{j=1}^{U_i}j^{n-R_i}(U_i-j)^{R_i-1}\\&=\prod_{i=1}^m\sum_{j=1}^{U_i}j^{n-R_i}\sum_{k=0}^{R_i-1}(-1)^k{R_i-1\choose k}j^kU_i^{R_i-1-k}\\&=\prod_{i=1}^m\sum_{j=1}^{U_i}\sum_{k=0}^{R_i-1}j^{n-R_i+k}(-1)^k{R_i-1\choose k}U_i^{R_i-1-k}\\&=\prod_{i=1}^m\sum_{k=0}^{R_i-1}(-1)^k{R_i-1\choose k}U_i^{R_i-1-k}\sum_{j=1}^{U_i}j^{n-R_i+k}\end{aligned}\]

注意到 \(Q=\sum\limits_{j=1}^{U_i}j^{n-R_i+k}\) 中的 \(U_i\) 会很大,显然不能直接枚举,我们可以插值解决。预处理出 \(S\) 的复杂度是 \(O(mn^2)\) 的。

最终答案就是

\[{n-1\choose k}g_{n-k-1}S\]

Code

#include <bits/stdc++.h>
using namespace std;
const int N = 233+5, yzh = 1e9+7; int n, m, k, U[N], R[N];
int fac[N], ifac[N];
int prime[N], tot, isprime[N], f[N], s1[N], s2[N]; int quick_pow(int a, int b) {
int ans = 1;
while (b) {
if (b&1) ans = 1ll*ans*a%yzh;
b >>= 1, a = 1ll*a*a%yzh;
}
return ans;
}
int C(int n, int m) {return 1ll*fac[n]*ifac[m]%yzh*ifac[n-m]%yzh; }
int lagrange(int *y, int k, int xi) {
int ans = 0; ++k;
s1[0] = xi, s2[k+1] = 1;
for (int i = 1; i <= k; i++) s1[i] = 1ll*s1[i-1]*(xi-i)%yzh;
for (int i = k; i >= 0; i--) s2[i] = 1ll*s2[i+1]*(xi-i)%yzh;
for (int i = 0; i <= k; i++)
(ans += 1ll*y[i]*(i == 0 ? 1 : s1[i-1])%yzh*s2[i+1]%yzh*ifac[i]%yzh*((k-i)&1 ? -1 : 1)*ifac[k-i]%yzh) %= yzh;
return ans;
}
int getQ(int xi, int k) {
tot = 0; memset(isprime, 1, sizeof(isprime));
isprime[1] = 0; f[0] = 0, f[1] = 1;
for (int i = 2; i <= k+1; i++) {
if (isprime[i]) prime[++tot] = i, f[i] = quick_pow(i, k);
for (int j = 1; j <= tot && i*prime[j] <= k+1; j++) {
isprime[i*prime[j]] = 0, f[i*prime[j]] = 1ll*f[i]*f[prime[j]]%yzh;
if (i%prime[j] == 0) break;
}
}
for (int i = 1; i <= k+1; i++) (f[i] += f[i-1]) %= yzh;
if (xi <= k+1) return f[xi];
return lagrange(f, k, xi);
}
int getS() {
int ans = 1;
for (int i = 1; i <= m; i++) {
int sum = 0;
for (int k = 0; k <= R[i]-1; k++)
if (k&1) (sum -= 1ll*C(R[i]-1, k)*quick_pow(U[i], R[i]-1-k)%yzh*getQ(U[i], n-R[i]+k)%yzh) %= yzh;
else (sum += 1ll*C(R[i]-1, k)*quick_pow(U[i], R[i]-1-k)%yzh*getQ(U[i], n-R[i]+k)%yzh) %= yzh;
ans = 1ll*ans*sum%yzh;
}
return ans;
}
void work() {
scanf("%d%d%d", &n, &m, &k);
fac[0] = fac[1] = ifac[0] = ifac[1] = 1;
for (int i = 2; i < N; i++) ifac[i] = -1ll*yzh/i*ifac[yzh%i]%yzh;
for (int i = 2; i < N; i++)
ifac[i] = 1ll*ifac[i-1]*ifac[i]%yzh,
fac[i] = 1ll*fac[i-1]*i%yzh;
for (int i = 1; i <= m; i++) scanf("%d", &U[i]);
for (int i = 1; i <= m; i++) scanf("%d", &R[i]);
int S = getS(), ans = 0;
for (int i = 1; i <= n-k-1; i++) {
int sum = 1;
for (int j = 1; j <= m; j++) sum = 1ll*sum*C(i, R[j]-1)%yzh;
if ((n-k-1-i)&1) (ans -= 1ll*sum*C(n-k-1, i)%yzh) %= yzh;
else (ans += 1ll*sum*C(n-k-1, i)%yzh) %= yzh;
}
ans = 1ll*ans*S%yzh*C(n-1, k)%yzh;
printf("%d\n", (ans+yzh)%yzh);
}
int main() {work(); return 0; }