稍微改动一下题面中定义的变量名。我们定义每门课的最高分为(h_i)(也就是题面中的(u_i))。定义被B神碾压的人数为(K)(而不是题面中的小写(k),这个小写(k)我们下面会有别的用处)。
题目要求恰好有(K)位同学被B神碾压。设答案为(f_K)。我们可以先求出至少有(k)位同学被碾压的情况数(g_{k})。那么根据二项式反演(也就是简单容斥)可知:
考虑求(g_k)。首先,当(k>min_{i=1}^{m}(n-r_i))时,显然不可能有(k)位同学被碾压,因此此时(g_k=0)。
其他的情况。我们不妨先写出一个朴素的式子,再考虑优化其复杂度。
首先钦定除B神外的(n-1)位同学中的(k)位是被碾压的。然后依次考虑每一门课。从没有被B神碾压的(n-k-1)位同学中,选出(n-r_i-k)位虽然没有全面被B神碾压,但是该门课得分小于等于B神的同学。枚举B神该门课的得分,这样就能根据排名情况求出其他人得分的方案数。写成式子就是:
暴力按照这个式子模拟,求(g)的时间复杂度(O(mh))。总时间复杂度(O(nmh))。期望得(40)分。
考虑优化。不难发现瓶颈在于求(sum_{j=1}^{h_i}j^{n-r_i}(h_i-j)^{r_i-1})。而这一部分又和(k)无关,所以可与对每个(i)预处理出(s_i=sum_{j=1}^{h_i}j^{n-r_i}(h_i-j)^{r_i-1})。把式子中的((h_i-j)^{r_i-1})用二项式定理展开,再把(j)的次幂合并到一起。得到:
于是问题进一步转化为求(sum_{j=1}^{h_i}j^{n-r_i+l})。发现(h_i)很大但(n-r_i+l)不大。所以这是一个典型的自然数幂求和问题。自然数幂求和的方法很多。例如,可以使用拉格朗日插值法。用这个方法,可以在(O(n))的时间里求出(sum_{j=1}^{h_i}j^{n-r_i+l})。于是求出单个(s_i)的时间复杂度优化为(O(n^2)),求出(s_{1dots m})的时间复杂度就是(O(mn^2))。后面用预处理好的(s_i),套一个二项式反演,可以(O(nm))求出答案。故总时间复杂度为(O(mn^2))。
参考代码(在LOJ查看):
//problem:LOJ2026
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const int MAXN=110;
const int MOD=1e9+7;
inline int mod1(int x){return x<MOD?x:x-MOD;}
inline int mod2(int x){return x<0?x+MOD:x;}
inline void add(int& x,int y){x=mod1(x+y);}
inline void sub(int& x,int y){x=mod2(x-y);}
inline int pow_mod(int x,int i){int y=1;while(i){if(i&1)y=(ll)y*x%MOD;x=(ll)x*x%MOD;i>>=1;}return y;}
int fac[MAXN+5],ifac[MAXN+5];
inline int comb(int n,int k){
assert(k>=0);
if(n<k)return 0;
return (ll)fac[n]*ifac[k]%MOD*ifac[n-k]%MOD;
}
void facinit(int lim=MAXN){
fac[0]=1;
for(int i=1;i<=lim;++i)fac[i]=(ll)fac[i-1]*i%MOD;
ifac[lim]=pow_mod(fac[lim],MOD-2);
for(int i=lim-1;i>=0;--i)ifac[i]=(ll)ifac[i+1]*(i+1)%MOD;
}
int n,m,K,h[MAXN+5],rk[MAXN+5],s[MAXN+5];
int main() {
facinit();
cin>>n>>m>>K;
for(int i=1;i<=m;++i)cin>>h[i];
int mn=n;
for(int i=1;i<=m;++i)cin>>rk[i],mn=min(mn,n-rk[i]);
static int pw[MAXN+5][MAXN+5];
for(int i=1;i<=MAXN;++i){
for(int j=0;j<=MAXN;++j){
pw[i][j]=pow_mod(i,j);
}
}
for(int i=1;i<=m;++i){
//预处理s[i]
/*
for(int j=1;j<=h[i];++j){
add(s[i],(ll)pow_mod(j,n-rk[i])*pow_mod(h[i]-j,rk[i]-1)%MOD);
}
*/
for(int l=0;l<=rk[i]-1;++l){
static int pre[MAXN+5],suf[MAXN+5];
int lim=n-rk[i]+l+2;
pre[0]=1;
suf[lim+1]=1;
for(int j=1;j<=lim;++j)pre[j]=(ll)pre[j-1]*mod2(h[i]%MOD-j)%MOD;
for(int j=lim;j>=1;--j)suf[j]=(ll)suf[j+1]*mod2(h[i]%MOD-j)%MOD;
int sum=0;
/*
for(int j=1;j<=h[i];++j){
add(sum,pow_mod(j,n-rk[i]+l));
}
*/
for(int j=1,y=0;j<=lim;++j){
//add(y,pow_mod(j,n-rk[i]+l));
add(y,pw[j][n-rk[i]+l]);
int tmp=(ll)y*pre[j-1]%MOD*suf[j+1]%MOD*ifac[j-1]%MOD*ifac[lim-j]%MOD;
if((lim-j)&1)sub(sum,tmp);
else add(sum,tmp);
}
int tmp=(ll)pow_mod(h[i],rk[i]-1-l)*comb(rk[i]-1,l)%MOD*sum%MOD;
if(l&1)sub(s[i],tmp);
else add(s[i],tmp);
}
}
int ans=0;
for(int k=K;k<=mn;++k){
int f=comb(n-1,k);
for(int i=1;i<=m;++i){
f=(ll)f*s[i]%MOD*comb(n-k-1,n-rk[i]-k)%MOD;
}
if((k-K)%2==1)sub(ans,(ll)comb(k,K)*f%MOD);
else add(ans,(ll)comb(k,K)*f%MOD);
}
cout<<ans<<endl;
return 0;
}