这题我好像理解了几天/kk。
分三步考虑:
第一部分计数
由于每个人都是不一样的,所以我们首先要枚举哪 (k) 个人被碾压,贡献是一个组合数 (inom {n-1} k)(B神自己不算在内)。
第二部分计数
现在我们要考虑对于每一门学科,哪些人比B神高,哪些人考的成绩小于等于B神。设恰有 (i) 个人没有被B神碾压的方案数为 (f_i),至多有 (i) 个人被B神碾压的方案数为 (g_i)。答案就是 (f_{n-k-1})。我们发现:
[g_i=sumlimits_{j=0}^i inom i j f_j
]
根据二项式反演可得:
[f_i=sumlimits_{j=0}^i (-1)^{i-j}inom i j g_j
]
然后这个 (g_i) 其实也非常好算:
[g_i=prodlimits_{i=1}^m inom {n-k-1}{r_i-1}
]
此部分时间复杂度 (O(n^2))
第三部分计数
现在对于每一门课,我们知道了每个人与B神相比的状态,只要再枚举分数就好了。设 (M_i) 表示第 (i) 门课的方案数(最后用乘法原理乘起来),暴力枚举B神的分数:
[egin{aligned}
M_i&=sumlimits_{i=1}^{U_i}(U_i-i)^{r_i-1} imes i^{n-r_i}\
&=sumlimits_{i=1}^{U_i} sumlimits_{j=1}^{r_i-1}inom {r_i-1} jU_i^j imes (-1)^{r_i-j-1} imes i^{n-j-1}\
&=sumlimits_{j=1}^{r_i-1}inom{r_i-1}{j}U_i^j imes (-1)^{r_i-j-1}sumlimits_{i=1}^{U_i} i^{n-j-1}
end{aligned}
]
发现后面那个东东是自然数幂和,所以可以拉格朗日插值直接爆算,时间复杂度 (O(n^3))。
最后把三个部分乘起来就行了qwq。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define int long long
using namespace std;
const int N=1009,M=1000000007;
int n,m,k,c[N][N],U[N],R[N],pre[N],suf[N],fac[N],inv_fac[N],d[N],cnt,a[N],p[N];
int ksm(int a,int b)
{
int res=1;
while(b)
{
if(b&1)
res=res*a%M;
b>>=1,a=a*a%M;
}
return res;
}
void init()
{
scanf("%lld %lld %lld",&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]);
for (int i=0;i<=100;i++)
c[i][0]=1;
for (int i=1;i<=100;i++)
for (int j=1;j<=i;j++)
c[i][j]=(c[i-1][j]+c[i-1][j-1])%M;
}
void prework(int k)
{
d[1]=1;cnt=0;
for (int i=2;i<=k+5;i++)
a[i]=0;
for (int i=2;i<=k+5;i++)
{
if(!a[i])
a[i]=i,p[++cnt]=a[i],d[i]=ksm(i,k);
for (int j=1;j<=cnt;j++)
{
if(p[j]>a[i]||p[j]>(k+5)/i)
break;
a[p[j]*i]=p[j],d[p[j]*i]=d[p[j]]*d[i]%M;
}
}
for (int i=2;i<=k+5;i++)
d[i]=(d[i]+d[i-1])%M;
}
int calc(int x,int k)
{
prework(k-1);
pre[0]=suf[k+2]=1;
for (int i=1;i<=k+1;i++)
pre[i]=pre[i-1]*(x-i)%M;
for (int i=k+1;i>=1;i--)
suf[i]=suf[i+1]*(x-i)%M;
fac[0]=1;
for (int i=1;i<=k+1;i++)
fac[i]=fac[i-1]*i%M;
inv_fac[k+1]=ksm(fac[k+1],M-2);
for (int i=k;i>=0;i--)
inv_fac[i]=inv_fac[i+1]*(i+1)%M;
int ans=0;
for (int i=1;i<=k+1;i++)
ans=(ans+d[i]*pre[i-1]%M*suf[i+1]%M*inv_fac[i-1]%M*inv_fac[k+1-i]%M*((k+1-i&1)?-1:1))%M;
return ans;
}
void work()
{
int ans1=0,ans2=1,d=n-k-1;
for (int i=d;i>=0;i--)
{
int tmp=((i-d)&1)?-1:1;
for (int j=1;j<=m;j++)
tmp=tmp*c[i][R[j]-1]%M;
ans1=(ans1+tmp*c[d][i]%M)%M;
}
for (int i=1;i<=m;i++)
{
int tmp=0;
for (int k=0;k<=R[i]-1;k++)
tmp=(tmp+ksm(U[i],k)*calc(U[i],n-k)%M*c[R[i]-1][k]%M*((R[i]-1-k&1)?-1:1))%M;
ans2=ans2*tmp%M;
}
printf("%lld
",(c[n-1][k]*ans1%M*ans2%M+M)%M);
}
signed main()
{
init();
work();
return 0;
}