Description
G系共有n位同学,M门必修课。这N位同学的编号为0到N-1的整数,其中B神的编号为0号。这M门必修课编号为0到M-
1的整数。一位同学在必修课上可以获得的分数是1到Ui中的一个整数。如果在每门课上A获得的成绩均小于等于B获
得的成绩,则称A被B碾压。在B神的说法中,G系共有K位同学被他碾压(不包括他自己),而其他N-K-1位同学则没
有被他碾压。D神查到了B神每门必修课的排名。这里的排名是指:如果B神某门课的排名为R,则表示有且仅有R-1
位同学这门课的分数大于B神的分数,有且仅有N-R位同学这门课的分数小于等于B神(不包括他自己)。我们需要
求出全系所有同学每门必修课得分的情况数,使其既能满足B神的说法,也能符合D神查到的排名。这里两种情况不
同当且仅当有任意一位同学在任意一门课上获得的分数不同。你不需要像D神那么厉害,你只需要计算出情况数模1
0^9+7的余数就可以了。
Input
第一行包含三个正整数N,M,K,分别表示G系的同学数量(包括B神),必修课的数量和被B神碾压的同学数量。第二
行包含M个正整数,依次表示每门课的最高分Ui。第三行包含M个正整数,依次表示B神在每门课上的排名Ri。保证1
≤Ri≤N。数据保证至少有1种情况使得B神说的话成立。N<=100,M<=100,Ui<=10^9
Output
仅一行一个正整数,表示满足条件的情况数模10^9+7的余数。
Sample Input
3 2 1
2 2
1 2
Sample Output
10
分析
首先,运用一发容斥原理,求出所有人与B神每门课分数相对关系的不同方案数。
这个似乎大(wo)家(lan)都(de)会(hui)了(yi),我就不说了,详见代码里的f。
然后,我们就需要计算每门课每个人的分数的方案数。对于每一门课,我们分别计算,然后把它们乘起来。
方便起见,令总分为s,名次为rk。
设B神的分数为x,则方案数为x^(n-rk)*(s-x)^(rk-1)
展开得到c(rk-1,0)*s^(rk-1)*x^(n-rk)-c(rk-1,1)*s^(rk-2)*x^(n-rk+1)+c(rk-1,2)*s^(rk-3)*x^(n-rk+2)-……..
显然,我们需要对于x=1..s的所有情况求和。
我们把x次数相同的项放在一起,进行一波整理,问题就转化成了求1^k+2^k+…+s^k
我们设g(k)=1^k+2^k+…+s^k,我们列出一波式子然后观察:
(s+1)^k-s^k=c(k,1)*s^(k-1) +c(k,2)*s^(k-2) +…+c(k,k)*s^0
s^k-(s-1)^k=c(k,1)(s-1)^(k-1)+c(k,2)(s-1)^(k-2)+…+c(k,k)*(s-1)^0
………………………………………………………………………………………………
2^k-1^k=c(k,1)*1^(k-1) +c(k,2)*1^(k-2) +...+c(k,k)*1^0
把这些式子全部相加,得到:
(s+1)^k-1=c(k,1)*g(k-1)+c(k,2)*g(k-2)+…+c(k,k)*g(0)
于是就可以通过递推的方式求出g
然后就做完了。
代码
#include <bits/stdc++.h>
typedef long long LL;
const int N = 105;
const int MOD = 1e9 + 7;
int n,m,k,jc[N],ny[N],po1[N],po2[N],f[N],u[N],r[N],g[N];
int ksm(int x,int y)
{
int ans = 1;
while (y)
{
if (y & 1)
ans = 1ll * ans * x % MOD;
x = 1ll * x * x % MOD;
y >>= 1;
}
return ans;
}
int C(int n,int m)
{
if (n < m)
return 0;
return 1ll * jc[n] * ny[m] % MOD * ny[n - m] % MOD;
}
int calc(int u,int r)
{
for (int i = 0; i <= n + 1; i++)
po1[i] = ksm(i,r - 1), po2[i] = ksm(i,n - r);
for (int i = 1; i <= n + 1; i++)
{
f[i] = 0;
for (int j = 1; j <= i; j++)
(f[i] += 1ll * po1[i - j] * po2[j] % MOD) %= MOD;
}
int ans = 0;
for (int i = 1; i <= n + 1; i++)
{
int s1 = f[i], s2 = 1;
for (int j = 1; j <= n + 1; j++)
if (i != j)
s1 = 1ll * s1 * (u - j) % MOD, s2 = 1ll * s2 * (i - j) % MOD;
(ans += 1ll * s1 * ksm(s2, MOD - 2) % MOD) %= MOD;
}
return ans;
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for (int i = 1; i <= m; i++)
scanf("%d",&u[i]);
for (int i = 1; i <= m; i++)
scanf("%d",&r[i]);
jc[0] = jc[1] = ny[0] = ny[1] = 1;
for (int i = 2; i <= n; i++)
jc[i] = 1ll * jc[i - 1] * i % MOD, ny[i] = 1ll * (MOD - MOD / i) * ny[MOD % i] % MOD;
for (int i = 2; i <= n; i++)
ny[i] = 1ll * ny[i] * ny[i - 1] % MOD;
for (int i = 1; i <= m; i++)
g[i] = calc(u[i],r[i]);
int ans = 0;
for (int i = k; i < n; i++)
{
int w = 1ll * C(n - 1, i) * C(i, k) % MOD;
for (int j = 1; j <= m; j++)
w = 1ll * w * C(n - i - 1, r[j] - 1) % MOD * g[j] % MOD;
if ((i - k) & 1)
(ans -= w) %= MOD;
else (ans += w) %= MOD;
}
printf("%d",(ans + MOD) % MOD);
}

本文介绍了一种解决特定计数问题的方法,该问题是关于在给定条件下计算学生在各课程中可能得分组合的数量。文章详细解释了如何使用容斥原理和组合数学技巧来求解,并提供了实现这一解决方案的代码。
1367

被折叠的 条评论
为什么被折叠?



