【BZOJ4559】[JLoi2016]成绩比较
Description
G系共有n位同学,M门必修课。这N位同学的编号为0到N-1的整数,其中B神的编号为0号。这M门必修课编号为0到M-1的整数。一位同学在必修课上可以获得的分数是1到Ui中的一个整数。如果在每门课上A获得的成绩均小于等于B获得的成绩,则称A被B碾压。在B神的说法中,G系共有K位同学被他碾压(不包括他自己),而其他N-K-1位同学则没有被他碾压。D神查到了B神每门必修课的排名。这里的排名是指:如果B神某门课的排名为R,则表示有且仅有R-1位同学这门课的分数大于B神的分数,有且仅有N-R位同学这门课的分数小于等于B神(不包括他自己)。我们需要求出全系所有同学每门必修课得分的情况数,使其既能满足B神的说法,也能符合D神查到的排名。这里两种情况不同当且仅当有任意一位同学在任意一门课上获得的分数不同。你不需要像D神那么厉害,你只需要计算出情况数模10^9+7的余数就可以了。
Input
第一行包含三个正整数N,M,K,分别表示G系的同学数量(包括B神),必修课的数量和被B神碾压的同学数量。第二行包含M个正整数,依次表示每门课的最高分Ui。第三行包含M个正整数,依次表示B神在每门课上的排名Ri。保证1≤Ri≤N。数据保证至少有1种情况使得B神说的话成立。N<=100,M<=100,Ui<=10^9
Output
仅一行一个正整数,表示满足条件的情况数模10^9+7的余数。
Sample Input
3 2 1
2 2
1 2
Sample Output
10
题解:本题可以分为两部分处理,答案等于两部分的方案数之积。
第一部分是在碾压K个人的前提下,所有人每门课的分数与B神分数的大小关系的方案数。不难想到容斥,用f[i]表示至少碾压了i个人的方案数,那么$f[i]=C_{n-1}^i\prod\limits_{j=1}^mC_{n-i-1}^{Rj-1}$。答案=至少碾压K个人-至少碾压K+1个人+至少碾压K+2个人。。。所以$ans1=\sum\limits_{i=K}^n(-1)^{K-i}C_i^kf[i]$。
第二部分是在已经确定所有人每门课与B神的相对关系的情况下,每个人得分的方案数。我们可以先分别计算每门课的方案数,最后将其乘起来。设当前课B神的排名为R,总分为U。一个比较暴力的方法就是我们枚举B神的得分x,那么方案数就是$x^{n-R}(U-x)^{R-1}$。所以这门课的总方案数就是:
$\sum\limits_{x=1}^Ux^{n-R}(U-x)^{R-1}\\=\sum\limits_{x=1}^U\sum\limits_{k=0}^{R-1}(-1)^kx^{n-1-k}U^k\\=\sum\limits_{k=0}^{R-1}(-1)^kU^k\sum\limits_{x=1}^Ux^{n-1-k}$
所以现在问题就在于如何快速求$\sum\limits_{i=1}^si^k$,我们设这个东西=g[k]。下面这步非常神:我们观察这个式子
$(s+1)^k-s^k=\sum\limits_{j=0}^{k-1}C_k^js^j\\s^k-(s-1)^k=\sum\limits_{j=0}^{k-1}C_k^j(s-1)^j\\...\\2^k-1^k=\sum\limits_{j=0}^{k-1}C_k^j1^j$
等式两边分别求和
$\sum\limits_{i=1}^s(i+1)^k-i^k=\sum\limits_{i=1}^{s}\sum\limits_{j=0}^{k-1}C_k^ji^j\\(s+1)^k-1=\sum\limits_{j=0}^{k-1}C_k^j\sum\limits_{i=1}^si^j=\sum\limits_{j=0}^{k-1}C_k^jg[j]$
将g[k-1]放到左面即可得
$g[k-1]=\frac {(s+1)^k-1-\sum\limits_{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; }