【题解】Luogu-P3270 JLOI 2016 成绩比较
式子有点长,步骤有点多,所以写一下。
题目要求恰好 \(k\) 人被吊打的方案数,容易想到二项式反演,设 \(f(k)\) 为钦定 \(k\) 人其他任意的方案数,\(g(k)\) 为恰好 \(k\) 人的方案数。基本式子:
\[f(k)=\sum_{i=k}^{n-1} \dbinom{i}{k} g(i)\Leftrightarrow g(k)=\sum_{i=k}^{n-1} \dbinom{i}{k} (-1)^{i-k} f(i)
\]
现在要求 \(f(k)\)。
从内层向外看,若在学科 \(i\) 得了 \(j\) 分,则有 \(n-R_i\) 人取值在 \([1,j]\) 间任意,\(R_i-1\) 人取值在 \([j+1,U_i]\) 间任意。这 \(n-R_i\) 人包括起初钦定的 \(k\) 人,剩下的部分不要求多个学科中相同。
于是对于学科 \(i\),可以得出一个:
\[\begin{aligned}
&\dbinom{n-1-k}{n-R_i-k}\sum_{j=1}^{U_i} j^{n-R_i}\times (U_i-j)^{R_i-1}\\
=&\dbinom{n-1-k}{n-R_i-k}\sum_{j=1}^{U_i} j^{n-R_i}\times \sum_{l=0}^{R_i-1} \dbinom{R_i-1}{l} U_i^l\times (-1)^{R_i-1-l} \times j^{R_i-1-l}\\
=&\dbinom{n-1-k}{n-R_i-k}\sum_{l=0}^{R_i-1}\dbinom{R_i-1}{l} (-1)^{R_i-1-l} U_i^l\times\sum_{j=1}^{U_i} j^{n-1-l}\\
=&\dbinom{n-1-k}{n-R_i-k}h(i)
\end{aligned}\]
后面与 \(k\) 无关就写作 \(h(i)\) 了,最后的自然数幂和可以 \(O(n)\) 求,于是求单个 \(h(i)\) 是 \(O(n^2)\) 的,所有 \(h(i)\) 是 \(O(n^2m)\) 的。
接下来只需要枚举钦定 \(k\) 人的方案以及将相互独立的每个学科之前方案数相乘:
\[f(k)=\dbinom{n-1}{k}\prod_{i=1}^m\dbinom{n-1-k}{n-R_i-k} h(i)
\]
最后套上二项式反演,即可求出 \(g(k)\)。
点击查看代码
int n,m,k;
int lim[105],rk[105];
int f[105],h[105];
inline int q_pow(int A,int B,int P){
int res=1;
while(B){
if(B&1) res=1ll*res*A%P;
A=1ll*A*A%P;
B>>=1;
}
return res;
}
int fact[105],fact_inv[105];
int C[105][105];
int Y[105][105];
int pre[105],suf[105];
inline int lagrange(int X,int K){
pre[0]=1,suf[K+1]=1;
for(int i=1;i<=K+1;++i) pre[i]=1ll*pre[i-1]*(X-(i-1)+mod)%mod;
for(int i=K;i>=0;--i) suf[i]=1ll*suf[i+1]*(X-(i+1)+mod)%mod;
int res=0;
for(int i=0;i<=K+1;++i){
int now=1ll*Y[K][i]*pre[i]%mod*suf[i]%mod*fact_inv[i]%mod*fact_inv[K+1-i]%mod;
if((K+1-i)&1) res=(res-now+mod)%mod;
else res=(res+now)%mod;
}
return res;
}
int ans;
int main(){
n=read(),m=read(),k=read();
for(int i=1;i<=m;++i) lim[i]=read();
for(int i=1;i<=m;++i) rk[i]=read();
C[0][0]=1;
for(int i=1;i<=100;++i){
C[i][0]=C[i][i]=1;
for(int j=1;j<i;++j){
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
}
}
fact[0]=1,fact_inv[0]=1;
for(int i=1;i<=100;++i) fact[i]=1ll*fact[i-1]*i%mod;
fact_inv[100]=q_pow(fact[100],mod-2,mod);
for(int i=99;i>=1;--i) fact_inv[i]=1ll*fact_inv[i+1]*(i+1)%mod;
for(int i=1;i<n+2;++i){
int now=1;
for(int j=0;j<n;++j){
Y[j][i]=(Y[j][i-1]+now)%mod;
now=1ll*now*i%mod;
}
}
for(int i=1;i<=m;++i){
int pw=1;
for(int j=0;j<rk[i];++j){
int now=1ll*C[rk[i]-1][j]*pw%mod*lagrange(lim[i],n-1-j)%mod;
if((rk[i]-1-j)&1) h[i]=(h[i]-now+mod)%mod;
else h[i]=(h[i]+now)%mod;
pw=1ll*pw*lim[i]%mod;
}
}
for(int i=k;i<n;++i){
f[i]=C[n-1][i];
for(int j=1;j<=m;++j){
f[i]=1ll*f[i]*C[n-1-i][n-rk[j]-i]%mod*h[j]%mod;
}
}
for(int i=k;i<n;++i){
int now=1ll*C[i][k]*f[i]%mod;
if((i-k)&1) ans=(ans-now+mod)%mod;
else ans=(ans+now)%mod;
}
printf("%d\n",ans);
return 0;
}