bzoj5339 [TJOI2018]教科书般的亵渎(伯努利数)

可以发现结果就是求若干 S(n,k)=i=1nik S ( n , k ) = ∑ i = 1 n i k
可以用伯努利数快速计算,见:portal

#include 
#include 
#include 
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 60
#define mod 1000000007
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline ll read(){
    ll x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int inv[N],C[N][N],B[N];ll a[N];
inline int cal(int n,int k){
    int res=0,tmp=n+1;
    for(int i=1;i<=k+1;++i)
        (res+=(ll)C[k+1][i]*B[k+1-i]%mod*tmp%mod)%=mod,tmp=(ll)tmp*(n+1)%mod;
    res=(ll)res*inv[k+1]%mod;return res;
}
inline int ksm(int x,int k){
    int res=1;for(;k;k>>=1,x=(ll)x*x%mod) if(k&1) res=(ll)res*x%mod;return res;
}
int main(){
//  freopen("defile.in","r",stdin);
//  freopen("defile.out","w",stdout);
    int tst=read();inv[1]=1;B[0]=1;
    for(int i=2;i<=52;++i) inv[i]=(ll)inv[mod%i]*(mod-mod/i)%mod;
    for(int i=0;i<=52;++i) C[i][0]=1;
    for(int i=1;i<=52;++i)
        for(int j=1;j<=i;++j) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
    for(int i=1;i<=51;++i){
        for(int j=0;j<=i-1;++j) (B[i]+=(ll)C[i+1][j]*B[j]%mod)%=mod;
        B[i]=(ll)B[i]*inv[i+1]*-1%mod;if(B[i]<0) B[i]+=mod;
    }while(tst--){
        int n=read()%mod,m=read(),res=0;
        for(int i=1;i<=m;++i) a[i]=read();sort(a+1,a+m+1);
        for(int i=1;i<=m;++i) a[i]%=mod;
        for(int i=0;i<=m;++i){
            (res+=cal(n-a[i],m+1))%=mod;
            for(int j=i+1;j<=m;++j)
                (res-=ksm(a[j]-a[i],m+1))%=mod;
        }printf("%d\n",res<0?res+mod:res);
    }return 0;
}

你可能感兴趣的:(bzoj,组合数学)