动态规划(计数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

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 }

 

  
  
posted @ 2016-10-27 21:47  TenderRun  阅读(1153)  评论(0编辑  收藏  举报