【BZOJ4559】[JLoi2016]成绩比较
Description
Input
Output
仅一行一个正整数,表示满足条件的情况数模10^9+7的余数。
Sample Input
2 2
1 2
Sample Output
题解:本题可以分为两部分处理,答案等于两部分的方案数之积。
第一部分是在碾压K个人的前提下,所有人每门课的分数与B神分数的大小关系的方案数。不难想到容斥,用f[i]表示至少碾压了i个人的方案数,那么$f[i]=C_{n-1}^iprodlimits_{j=1}^mC_{n-i-1}^{Rj-1}$。答案=至少碾压K个人-至少碾压K+1个人+至少碾压K+2个人。。。所以$ans1=sumlimits_{i=K}^n(-1)^{K-i}C_i^kf[i]$。
第二部分是在已经确定所有人每门课与B神的相对关系的情况下,每个人得分的方案数。我们可以先分别计算每门课的方案数,最后将其乘起来。设当前课B神的排名为R,总分为U。一个比较暴力的方法就是我们枚举B神的得分x,那么方案数就是$x^{n-R}(U-x)^{R-1}$。所以这门课的总方案数就是:
$sumlimits_{x=1}^Ux^{n-R}(U-x)^{R-1}\=sumlimits_{x=1}^Usumlimits_{k=0}^{R-1}(-1)^kC_{R-1}^kU^{R-1-k}x^{n-R+k}\=sumlimits_{k=0}^{R-1}(-1)^kC_{R-1}^kU^{R-1-k}sumlimits_{x=1}^Ux^{n-R+k}$
所以现在问题就在于如何快速求$sumlimits_{i=1}^si^k$,我们设这个东西=g[k]。下面这步非常神:我们观察这个式子
$(s+1)^k-s^k=sumlimits_{j=0}^{k-1}C_k^js^j\s^k-(s-1)^k=sumlimits_{j=0}^{k-1}C_k^j(s-1)^j\...\2^k-1^k=sumlimits_{j=0}^{k-1}C_k^j1^j$
等式两边分别求和
$sumlimits_{i=1}^s(i+1)^k-i^k=sumlimits_{i=1}^{s}sumlimits_{j=0}^{k-1}C_k^ji^j\(s+1)^k-1=sumlimits_{j=0}^{k-1}C_k^jsumlimits_{i=1}^si^j=sumlimits_{j=0}^{k-1}C_k^jg[j]$
将g[k-1]放到左面即可得
$g[k-1]=frac {(s+1)^k-1-sumlimits_{j=0}^{k-2}C_k^jg[j]} {C_k^{k-1}}$
递推求出g[k]即可。
时间复杂度$O(n^3)$。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; typedef long long ll; const ll P=1000000007; int n,m,K; ll ans1,ans2; int R[110]; ll c[110][110],f[110],U[110],g[110]; inline ll pm(ll x,ll y) { ll z=1; while(y) { if(y&1) z=z*x%P; x=x*x%P,y>>=1; } return z; } inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<'0'||gc>'9') {if(gc=='-') f=-f; gc=getchar();} while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar(); return ret*f; } int main() { n=rd(),m=rd(),K=rd(),ans2=1; int i,j,k; for(i=0;i<=max(n,m);i++) { c[i][0]=1; for(j=1;j<=i;j++) c[i][j]=(c[i-1][j-1]+c[i-1][j])%P; } for(i=1;i<=m;i++) U[i]=rd(); for(i=1;i<=m;i++) R[i]=rd(); for(i=n-1;i>=K;i--) { f[i]=c[n-1][i]; for(j=1;j<=m;j++) f[i]=f[i]*c[n-i-1][n-R[j]-i]%P; ans1=(ans1+(((i^K)&1)?-1:1)*f[i]*c[i][K]%P+P)%P; } for(i=1;i<=m;i++) { ll tmp=0; g[0]=U[i]; for(k=1;k<=n;k++) { g[k]=(pm(U[i]+1,k+1)-1+P)%P; for(j=0;j<k;j++) g[k]=(g[k]-c[k+1][j]*g[j]%P+P)%P; g[k]=g[k]*pm(c[k+1][k],P-2)%P; } for(j=0;j<=R[i]-1;j++) tmp=(tmp+((j&1)?-1:1)*c[R[i]-1][j]*pm(U[i],R[i]-j-1)%P*g[n-R[i]+j]%P+P)%P; ans2=ans2*tmp%P; } printf("%lld",ans1*ans2%P); return 0; }