传送门
数据范围: n ≤ 100 n\le100 n≤100, m ≤ 100 m\le100 m≤100, u i ≤ 1 0 9 u_i\le10^9 ui≤109。
我总是想不到这种计数类问题。。。
令 f [ i ] [ j ] f[i][j] f[i][j] 表示在前 i i i 门课,被B神碾压的同学数为 j j j 的方案数,那么有转移方程:
f [ i ] [ j ] = ∑ k = j n f [ i − 1 ] [ k ] ( k k − j ) ( n − k − 1 r i − 1 − ( k − j ) ) d i f[i][j]=\sum_{k=j}^nf[i-1][k]\binom k {k-j}\binom {n-k-1}{r_i-1-(k-j)}d_i f[i][j]=k=j∑nf[i−1][k](k−jk)(ri−1−(k−j)n−k−1)di
其中 d i d_i di 表示在第 i i i 门课上分配成绩的方案数,我们先不管它。
这个转移的意思就是,由于被碾压的人数从 k k k 减为了 j j j,说明就要从这 k k k 个人中选出 ( k − j ) (k-j) (k−j) 个人成绩比B神高;又由于本身有 ( r i − 1 ) (r_i-1) (ri−1) 个人成绩比B神高,所以要从剩下的 ( n − k − 1 ) (n-k-1) (n−k−1) 个人中选 r i − 1 − ( k − j ) r_i-1-(k-j) ri−1−(k−j) 个。
现在考虑计算 d i d_i di,我们可以直接枚举B神的分数,也就是:
d i = ∑ j = 1 u i j n − r i ( u i − j ) r i − 1 d_i=\sum_{j=1}^{u_i}j^{n-r_i}(u_i-j)^{r_i-1} di=j=1∑uijn−ri(ui−j)ri−1
但是 u i u_i ui 是 1 0 9 10^9 109 级别的,直接枚举显然不可行。其实可以把 d i d_i di 看做是关于 u i u_i ui 的多项式,然么根据自然数幂和的结论,这个多项式的次数是不超过 r i r_i ri 的(也就是不超过 n n n 的),我们可以暴力算出 ( n + 1 ) (n+1) (n+1) 个点值然后拉格朗日插值即可。
时间复杂度 O ( n 3 ) O(n^3) O(n3)。
#include
#include
#include
#define N 105
#define P 1000000007
using namespace std;
int n,m,K;
int u[N],r[N],y[N],c[N][N],f[N][N],Pow[N][N];
int add(int x,int y) {return x+y>=P?x+y-P:x+y;}
int dec(int x,int y) {return x-y< 0?x-y+P:x-y;}
int mul(int x,int y) {return 1ll*x*y%P;}
int power(int a,int b,int ans=1){
for(;b;b>>=1,a=mul(a,a))
if(b&1) ans=mul(ans,a);
return ans;
}
void prework(){
for(int i=0;i<N;++i){
c[i][0]=c[i][i]=1;
for(int j=1;j<i;++j) c[i][j]=add(c[i-1][j-1],c[i-1][j]);
}
Pow[0][0]=1;
for(int i=1;i<N;++i){
Pow[i][0]=1,Pow[i][1]=i;
for(int j=2;j<N;++j) Pow[i][j]=mul(Pow[i][j-1],i);
}
}
int calc(int u,int r){
memset(y,0,sizeof(y));
for(int i=1;i<=n+1;++i)
for(int j=1;j<=i;++j)
y[i]=add(y[i],mul(Pow[j][n-r],Pow[i-j][r-1]));
int ans=0;
for(int i=1;i<=n+1;++i){
int a=y[i],b=1;
for(int j=1;j<=n+1;++j){
if(i==j) continue;
a=mul(a,dec(u,j)),b=mul(b,dec(i,j));
}
ans=add(ans,mul(a,power(b,P-2)));
}
return ans;
}
int C(int n,int m){
return (n<0||m<0||n<m)?0:c[n][m];
}
int main(){
prework();
scanf("%d%d%d",&n,&m,&K);
for(int i=1;i<=m;++i) scanf("%d",&u[i]);
for(int i=1;i<=m;++i) scanf("%d",&r[i]);
f[0][n-1]=1;
for(int i=1;i<=m;++i){
int val=calc(u[i],r[i]);
for(int j=K;j<n;++j)
for(int k=j;k<n;++k)
f[i][j]=add(f[i][j],mul(mul(val,f[i-1][k]),mul(C(k,k-j),C(n-k-1,r[i]-1-(k-j)))));
}
printf("%d\n",f[m][K]);
return 0;
}