[JLOI2016]成绩比较(容斥+拉格朗日插值)
要求的是三部分:
1.只考虑从所有人中选出K个人被碾压的方案数
2.只考虑所有人每门成绩高低关系(高于B神或低于B神)的方案数
3.只考虑所有人的具体成绩方案数
答案显然是三数相乘
(下文中下标均从1开始,比如\(文中U_i表示题中U_{i-1}\))
第一部分
显然是\(C_{N-1}^K\)
第二部分
每门都要有\(R-1\)个人的成绩超过B神
考虑\(F(x)=\prod\limits_{i=1}^MC_x^{R_i-1}\)表示每门有R-1个人超过B神的方案数
设d=N-K-1
由容斥得\(G=\sum\limits_{i=0}^d (-1)^{d-i}C_d^i F(i)\)表示d个人不被碾压的方案数
第三部分
首先只考虑一门的方案数
a个人分布在b分里的方案数为\(b^a\)
先考虑B神分数为x时的方案数为\(x^{N-R}(U-x)^{R-1}\)
所以第 i 门的方案数为\(H(i)=\sum\limits_{x=1}^{U_i} x^{N-R_i} (U_i-x)^{R_i-1}\)
把\((U_i-x)^{R_i-1}\)二项式定理展开得到\(\sum\limits_{t=0}^{R_i-1}(-x)^t U_i^{R_i-t-1}C_{R_i-1}^t\)
\[\therefore H(i)
=\sum\limits_{x=1}^{U_i}x^{N-R_i}\sum\limits_{t=0}^{R_i-1}(-1)^tx^tU_i^{R_i-t-1}C_{R_i-1}^t\\
=\sum\limits_{x=1}^{U_i}\sum\limits_{t=0}^{R_i-1}(-1)^tx^{N-R_i}x^tU_i^{R_i-t-1}C_{R_i-1}^t\\
=\sum\limits_{x=1}^{U_i}\sum\limits_{t=0}^{R_i-1}(-1)^tx^{N-R_i+t}U_i^{R_i-t-1}C_{R_i-1}^t\\
=\sum\limits_{t=0}^{R_i-1}\sum\limits_{x=1}^{U_i}(-1)^tx^{N-R_i+t}U_i^{R_i-t-1}C_{R_i-1}^t\\
=\sum\limits_{t=0}^{R_i-1}(-1)^tU_i^{R_i-t-1}C_{R_i-1}^t\sum\limits_{x=1}^{U_i}x^{N-R_i+t}\\
\]
其中\(\sum\limits_{x=1}^{U_i}x^{N-R_i+t}\)是连续自然数幂和
根据定理: 前N个自然数的K次幂和一定能表示为关于N的K+1次多项式
就可以用拉格朗日插值在\(O(N)\)的时间内算出
那么M门加一起的方案数是\(\prod\limits_{i=1}^M H(i)\), 时间复杂度\(O(N^2M)\)
结论
\(ans=C_{N-1}^K G\prod\limits_{i=1}^MH(i)\)
代码
#include<bits/stdc++.h>
const int N=305;
const int mod=1e9+7;
int Pow(int x,int f=mod-2){
int re=1;
while(f){
if(f&1)re=1ll*re*x%mod;
f>>=1;
x=1ll*x*x%mod;
}
return re;
}
namespace Lagrange{
int mul[N];
int Lagrange(int n,int t,int *f){
if(t<=n)return f[t];
int re=0,s=1;
for(int i=1;i<=n;i++)s=1ll*s*(t-i)%mod;
for(int i=1;i<=n;i++){
int tmp=1ll*s*f[i]%mod;
int div=1ll*mul[i-1]*mul[n-i]%mod;
div=1ll*div*(t-i)%mod;
div=Pow(div);
tmp=1ll*tmp*div%mod;
if((n-i)&1)re=(re-tmp+mod)%mod;
else re=(re+tmp)%mod;
}
return re;
}
void Pre(int t,int *f){
mul[0]=mul[1]=1;
for(int i=2;i<=t+2;i++)mul[i]=1ll*mul[i-1]*i%mod;
for(int i=1;i<=t+2;i++)f[i]=(f[i-1]+Pow(i,t))%mod;
}
int f[N];
int Sum(int n,int t){
Pre(t,f);
int re=Lagrange(t+2,n,f);
return re;
}
}
int n,m,k,u[N],r[N],c[N][N];
void Init(){
c[0][0]=1;
for(int i=1;i<=n;i++){
c[i][0]=1;
for(int j=1;j<=i;j++){
c[i][j]=c[i-1][j-1]+c[i-1][j];
c[i][j]%=mod;
}
}
}
int Count(int x){
int re=1;
for(int i=1;i<=m;i++)
re=1ll*re*c[x][r[i]-1]%mod;
return re;
}
signed 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);
Init();
int ans=0,tmp,d=n-k-1;
for(int i=0;i<=d;i++){
int tmp=((d-i)&1)?mod-1:1;
tmp=1ll*tmp*c[d][i]%mod;
tmp=1ll*tmp*Count(i)%mod;
ans=(ans+tmp)%mod;
}
for(int i=1,s;i<=m;i++){
s=0;
for(int t=0;t<r[i];t++){
tmp=(t&1)?mod-1:1;
tmp=1ll*tmp*Pow(u[i],r[i]-t-1)%mod;
tmp=1ll*tmp*c[r[i]-1][t]%mod;
tmp=1ll*tmp*Lagrange::Sum(u[i],n+t-r[i])%mod;
s=(s+tmp)%mod;
}
ans=1ll*ans*s%mod;
}
ans=1ll*ans*c[n-1][k]%mod;
printf("%d",ans);
return 0;
}