动态规划(计数DP):JLOI 2016 成绩比较
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神那么厉害,你只需要计算出情况数模1
0^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
2 2
1 2
Sample Output
10
首先发现对于n个确定排名的人,分数和其排名无关,这里可以分开处理。
有两种做法,第一种我分成三个集合,1:我 2:分数小于等于我的3:分数大于等于我的。
设f[i]表示完虐至少i人,三个集合内无名次顺序的方案数,可以发现容斥是能很好地解决这个分问题的。
转移是f[i]=C(n-1,i)*Σj=1,j<=mC(n-i-1,r[j]-1)-Σj=i+1,j<=nf[j]*C(j,i) 这个很容易推得。
然后发现每门的得分情况互不相关,符合乘法原理,我们分开处理,设当前科目排名r,得分上限u,我的得分是x。方案数是 xn-r(u-x)r-1 但是u太大了,x的取值是[1,u],不可能枚举得来,还得分析。
设t[x]=xn-r(u-x)r-1
=xn-r *(C(r-1,r-1)*ur-1-C(r-1,r-2)*ur-2*x+……+C(r-1,0)*xr-1)
=C(r-1,r-1)*ur-1*xn-r-C(r-1,r-2)*ur-2*xn-r+1+……+C(r-1,0)*xn-1
x=1:请自动脑补
x=2:请自动脑补
x=3:请自动脑补
……
x=u:请自动脑补
设g[k]=1k+2k+3k+……+uk
Σx=1,x<=ut[x]=C(r-1,r-1)*ur-1*g[n-r]-C(r-1,r-2)*ur-2*xn-r+1*g[n-r+1]+……+C(r-1,0)*xn-1*g[n-1]
现在只要知道如何求g[k],考虑递推。
发现设p[a+1]=(a+1)k=C(k,k)*ak+C(k,k-1)*ak-1+……+C(k,0)*a0
Σa=1,a<=up[a+1]=g[k]+(u+1)k-1=C(k,k)*g[k]+C(k,k-1)*g[k-1]+……+C(k,0)*g[0]
可以由 (u+1)k-1=C(k,k-1)*g[k-1]+……+C(k,0)*g[0]
移项得 g[k-1]=(u+1)k-1-(C(k,k-2)*g[k-2]+C[k,k-3]*g[k-3]+……+C[k,0]*g[0])
用k=k+1替入得:g[k]=(u+1)k+1-1-(C(k+1,k-1)*g[k-1]+C[k+1,k-2]*g[k-2]+……+C[k+1,0]*g[0])
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 const int N=110,Mod=1000000007; 6 int n,m,K,u[N],r[N]; 7 typedef long long LL; 8 LL fac[N],ifac[N],c[N][N],f[N],g[N]; 9 LL Inv(LL x){return x==1?1:(Mod-Mod/x)*Inv(Mod%x)%Mod;} 10 LL C(int x,int y){ 11 if(x<y||x<0||y<0)return 0; 12 return fac[x]*ifac[y]%Mod*ifac[x-y]%Mod; 13 } 14 15 LL Quick_pow(LL x,LL y){ 16 LL ret=1; 17 while(y){ 18 if(y&1)ret=ret*x%Mod; 19 y>>=1;x=x*x%Mod; 20 } 21 return ret; 22 } 23 24 int main(){ 25 scanf("%d%d%d",&n,&m,&K); 26 for(int i=1;i<=m;i++) 27 scanf("%d",&u[i]); 28 for(int i=1;i<=m;i++) 29 scanf("%d",&r[i]); 30 fac[0]=ifac[0]=1; 31 for(int i=1;i<N;i++){ 32 fac[i]=fac[i-1]*i%Mod; 33 ifac[i]=Inv(fac[i]); 34 } 35 for(int i=n-1;i>=K;i--){ 36 f[i]=C(n-1,i); 37 for(int j=1;j<=m;j++) 38 (f[i]*=C(n-i-1,r[j]-1))%=Mod; 39 for(int j=i+1;j<n;j++){ 40 (f[i]-=f[j]*C(j,i))%=Mod; 41 (f[i]+=Mod)%=Mod; 42 } 43 } 44 LL ans=f[K]; 45 for(int i=1;i<=m;i++){ 46 LL R=r[i],U=u[i]; 47 g[0]=U; 48 for(int k=1;k<=n;k++){ 49 g[k]=Quick_pow(U+1,k+1)-1; 50 for(int j=k-1;j>=0;j--) 51 (g[k]+=Mod-C(k+1,j)*g[j]%Mod)%=Mod; 52 (g[k]*=Inv(C(k+1,k)))%=Mod; 53 } 54 LL tmp=0,rmp; 55 for(int j=1,s=1;j<=R;s*=-1,j++){ 56 rmp=(s+Mod)*C(R-1,j-1)%Mod; 57 (rmp*=Quick_pow(U,R-j))%=Mod; 58 (rmp*=g[n-R+j-1])%=Mod; 59 (tmp+=rmp)%=Mod; 60 } 61 ans=ans*tmp%Mod; 62 } 63 printf("%lld\n",ans); 64 return 0; 65 }
尽最大的努力,做最好的自己!