Description:
G
G
系共有位同学,
M
M
门必修课。这位同学的编号为
0
0
到的整数,其中B神的编号为
0
0
号。这M门必修课编号为到
M−1
M
−
1
的整数。一位同学在必修课上可以获得的分数是
1
1
到中的一个整数。如果在每门课上
A
A
获得的成绩均小于等于B获得的成绩,则称被
B
B
碾压。在神的说法中,
G
G
系共有位同学被他碾压(不包括他自己),而其他
N−K−1
N
−
K
−
1
位同学则没有被他碾压。
D
D
神查到了B神每门必修课的排名。这里的排名是指:如果神某门课的排名为
R
R
,则表示有且仅有R-1位同学这门课的分数大于神的分数,有且仅有
N−R
N
−
R
位同学这门课的分数小于等于
B
B
神(不包括他自己)。我们需要求出全系所有同学每门必修课得分的情况数,使其既能满足神的说法,也能符合
D
D
神查到的排名。这里两种情况不同当且仅当有任意一位同学在任意一门课上获得的分数不同。你不需要像神那么厉害,你只需要计算出情况数模
109+7
10
9
+
7
的余数就可以了。
Solution:
需要计算两个部分,两个部分乘起来即是答案。
第一个部分是碾压
k
k
个人且满足排名的情况下所有人的排名与的排名的大小关系的方案数。第二个部分则是每个人分数满足条件的方案数,两个部分乘起来即是答案,因为每种大小关系都可以映射到对应的分数。
第一部分容斥计算,恰好
k
k
个转化为至少个是常用技巧。用
f[i]
f
[
i
]
表示至少碾压i个人的方案数,那么
f[i]=Cin−1∗∏mj=1Crnk[j]n−i−1
f
[
i
]
=
C
n
−
1
i
∗
∏
j
=
1
m
C
n
−
i
−
1
r
n
k
[
j
]
,所以
ans1=∑n−1i=kCki∗(−1)i−k∗f[i]
a
n
s
1
=
∑
i
=
k
n
−
1
C
i
k
∗
(
−
1
)
i
−
k
∗
f
[
i
]
第二部分考虑枚举
B
B
的分数,那么对于一个
x
x
方案数是
ans2=∑mi=1∑u[i]x=1xn−rnk[i]∗(u−x)rnk[i]−1
a
n
s
2
=
∑
i
=
1
m
∑
x
=
1
u
[
i
]
x
n
−
r
n
k
[
i
]
∗
(
u
−
x
)
r
n
k
[
i
]
−
1
二项式定理展开
(u−x)rnk[i]−1
(
u
−
x
)
r
n
k
[
i
]
−
1
,那么对于每门课的方案数是
∑ux=1∑rnk−1i=1ui∗Cirnk−1∗xn−i−1∗(−1)rnk−i−1
∑
x
=
1
u
∑
i
=
1
r
n
k
−
1
u
i
∗
C
r
n
k
−
1
i
∗
x
n
−
i
−
1
∗
(
−
1
)
r
n
k
−
i
−
1
交换求和顺序
∑rnk−1i=1ui∗Cirnk−1∗(−1)rnk−i−1∗∑ux=1xn−i−1
∑
i
=
1
r
n
k
−
1
u
i
∗
C
r
n
k
−
1
i
∗
(
−
1
)
r
n
k
−
i
−
1
∗
∑
x
=
1
u
x
n
−
i
−
1
预处理伯努利数求
∑ux=1xn−i−1
∑
x
=
1
u
x
n
−
i
−
1
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 205, P = 1e9 + 7;
int n, m, k;
ll ans1, ans2 = 1;
int u[N], rnk[N];
ll c[N][N], s[N][N], inv[N], b[N];
ll power(ll x, ll t) {
ll ret = 1;
for(; t; t >>= 1, x = x * x % P) {
if(t & 1) {
ret = ret * x % P;
}
}
return ret;
}
ll calc(int n, int k) {
ll fac = 1, ans = 0;
for(int i = 1; i <= k + 1; ++i)
{
fac = fac * (n + 1) % P;
ans = (ans + c[k + 1][i] * b[k + 1 - i] % P * fac % P) % P;
}
ans = ans * inv[k + 1] % P;
return ans;
}
int main() {
scanf("%d%d%d", &n, &m, &k);
c[0][0] = 1;
s[0][0] = 1;
for(int i = 1; i <= 200; ++i) {
c[i][0] = 1;
for(int j = 1; j <= i; ++j) {
c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % P;
}
}
for(int i = 1; i <= m; ++i) {
scanf("%d", &u[i]);
}
for(int i = 1; i <= m; ++i) {
scanf("%d", &rnk[i]);
}
inv[1] = 1;
for(int i = 1; i < N; ++i) {
if(i != 1) inv[i] = (P - P / i) * inv[P % i] % P;
}
b[0] = 1;
for(int i = 1; i < N - 1; ++i) {
for(int j = 0; j < i; ++j) {
b[i] = (b[i] + c[i + 1][j] * b[j]) % P;
}
b[i] = ((b[i] * -inv[i + 1] % P) + P) % P;
}
for(int i = k; i < n; ++i) {
ll tmp = c[i][k] * (((i - k) & 1) ? -1 : 1);
tmp = (tmp % P + P) % P;
for(int j = 1; j <= m; ++j) {
tmp = tmp * c[n - i - 1][rnk[j] - 1] % P;
}
ans1 = (ans1 + tmp * c[n - 1][i] % P) % P;
}
for(int i = 1; i <= m; ++i) {
ll tmp = 0, pw = 1;
for(int j = 0; j < rnk[i]; ++j, pw = pw * u[i] % P) {
tmp = (tmp + power(u[i], j) * c[rnk[i] - 1][j] % P * calc(u[i], n - j - 1) * (((rnk[i] - j - 1) & 1) ? -1 : 1) % P) % P;
tmp = (tmp + P) % P;
}
ans2 = ans2 * tmp % P;
}
printf("%lld\n", ans1 * ans2 % P);
return 0;
}