计数类的问题,有点难想
先考虑满足每一科排名的关系:枚举每门课,
由于 B神只碾压了 k 个人,有 个人没有碾压,令 ,考虑先选出这 个人,每一科比 B神 高的人 只在这 个人里选,但要满足这 个人 至少有一门 比 B神高,先考虑忽略这个条件:
令
,这部分不一定 t 个人 都有一门比 B 神高。
这个组合数学模型可以转化为:有
个盒子,每次放
个小球,共放
次,盒子不能为空,有多少种方案。
考虑用容斥得到答案:枚举有几个空盒子进行容斥,也可以用二项式反演:
设
表示在 t 个盒子中一共放 m 次,每次放
个小球,盒子可以为空的方案总数,
为
加上一条限制:每个盒子都不能为空的方案总数。
显然 ,二项式反演得到 :
也可以枚举空盒进行容斥:
反演时等号两边的参数形式不变,
的幂次和等号右边的参数之和等于
这样可以得到仅考虑每一门排名的方案数:
接下来考虑不同分数的方案数,考虑枚举分数, ,由于 非常大,观察这个式子,是一个 为自变量的 次多项式和 次多项式的乘积之和,显然这两个多项式的乘积得到的是 为自变量的 次多项式,它的和又得到 以 为自变量的 多项式,使用拉格朗日插值插入 个点
最后的答案就是:
后面部分可以 预处理插值(预处理幂次可以降低到 ),总体复杂度为
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 10;
const int mod = 1e9 + 7;
typedef long long ll;
int mx;
inline ll add(ll x, ll y) {
x += y;
if (x >= mod) x -= mod;
return x;
}
inline ll sub(ll x, ll y) {
x -= y;
if (x < 0) x += mod;
return x;
}
inline ll mul(ll x, ll y) {
return x * y % mod;
}
ll fpow(ll a,ll b) {
ll r = 1;
while(b) {
if (b & 1) r = mul(r,a);
b >>= 1;
a = mul(a,a);
}
return r;
}
ll g[maxn],f[maxn],u[maxn],r[maxn],k,n,m;
ll fac[maxn],ifac[maxn];
ll cal(ll g[maxn],ll x) { //拉格朗日插值计算多项式
if (x <= mx) return g[x];
ll tmp = 1,inv,ans = 0;
for (int i = 1; i <= mx; i++)
tmp = mul(tmp,x - i);
for (int i = 1; i <= mx; i++) {
ll res = 1, inv = fpow(x - i,mod - 2);
res = mul(res,g[i]);
res = mul(res,ifac[i - 1]);
res = mul(res,ifac[mx - i]);
res = mul(res,inv);
res = mul(res,tmp);
if ((mx - i) & 1) res = mul(res,-1);
if (res < 0) res += mod;
ans = add(ans,res);
}
return ans;
}
ll C(int x,int y) {
if (y > x || x < 0) return 0;
return mul(mul(ifac[y],ifac[x - y]),fac[x]);
}
int main() {
fac[0] = 1;
for (int i = 1; i <= 1000; i++)
fac[i] = mul(fac[i - 1],i);
ifac[1000] = fpow(fac[1000],mod - 2);
for (int i = 1000 - 1; i >= 0; i--)
ifac[i] = mul(ifac[i + 1],i + 1);
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]);
}
ll ans = 0;
mx = n + 2;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= mx; j++) {
g[j] = 0;
for (int k = 1; k <= j; k++) {
ll t1 = fpow(k,n - r[i]), t2 = fpow(sub(j,k),r[i] - 1);
g[j] = add(g[j],mul(t1,t2));
}
}
f[i] = cal(g,u[i]);
}
for (int t = 0; t <= n - k - 1; t++) {
ll res = C(n - k - 1,t);
if ((n - k - 1 - t) & 1) res = mod - res;
for (int i = 1; i <= m; i++) {
res = mul(res,C(t,r[i] - 1));
res = mul(res,f[i]);
}
ans = add(ans,res);
}
ans = mul(ans,C(n - 1,n - k - 1));
printf("%lld\n",ans);
return 0;
}