[BZOJ5339]-[TJOI2018]教科书般的亵渎-自然数幂和

说在前面

me讨厌数学题!
讨厌讨厌讨厌死了!


题目

BZOJ5339传送门
看题可进传送门


解法

题目描述有点不清真
其实稍微想一想就能发现,这个题就是要我们求自然数幂和
假设我们已经会求自然数幂和了,这道题大概就这样做:枚举当前是第几次亵渎,记亵渎之前最高血量为 q q ,把 q 拿出来求一次幂和,然后把那些不存在的怪的贡献用快速幂减掉。这样复杂度就是 Θ(m2log2m+mδ) Θ ( m 2 log 2 ⁡ m + m ∗ δ ) δ δ 是指求幂和的复杂度

然后我们来考虑如何做幂和,我们要求的东西是 ni=0im ∑ i = 0 n i m ,我们记这个东西为 Smn S n m
看见 im i m ,不难想到使用 斯特林数/二项式展开 等技巧,这里我们使用二项式展开
那么得到( C C 是组合数)

Snm=i=0nim=i=0n(j=0mCmj(i1)mj)=j=0mCmj(i=0n(i1)mj)=j=0mCmjSn1mj

我们貌似得到一个有点像递推式的东西,我们把 n n 都加上 1,得到

Smn+1=j=0mCjmSmjn S n + 1 m = ∑ j = 0 m C m j S n m − j

但是我们发现它的下标变化了,这显然不是我们想要的
不难发现 Smn+1=Smn+(n+1)m S n + 1 m = S n m + ( n + 1 ) m ,把它和上面那个式子联立起来,于是递推式就只和 m m 有关了,我们继续推导,得到:

Snm+(n+1)m=j=0mCmjSnmjSnm+(n+1)m=j=1mCmjSnmj+Snm(n+1)m=j=2mCmjSnmj+Cm1Snm1

同时把m指标加1,得到
(n+1)m+1Smn=j=2m+1Cjm+1Sm+1jn+C1m+1Smn=(n+1)m+1m+1j=2Cjm+1Sm+1jnm+1 ( n + 1 ) m + 1 = ∑ j = 2 m + 1 C m + 1 j S n m + 1 − j + C m + 1 1 S n m S n m = ( n + 1 ) m + 1 − ∑ j = 2 m + 1 C m + 1 j S n m + 1 − j m + 1

然后我们成功地得到了一个递推式!这个递推式是 m2 m 2 的,可以通过此题
注意,我们在推导过程中使用了 二项式展开 ,因此需要定义 00=1 0 0 = 1 ,这是由于 (01+1)0=0i=0Ci0(1)0=1 ( 0 − 1 + 1 ) 0 = ∑ i = 0 0 C 0 i ( − 1 ) 0 = 1
并且在我们第一步推导的如下步骤中,当 m=j,i=0 m = j , i = 0 时,贡献也会变为 (1)0=1 ( − 1 ) 0 = 1 ,因此必须如上定义才能保证正确

j=0mCjm(i=0n(i1)mj)=j=0mCjmSmjn ∑ j = 0 m C m j ( ∑ i = 0 n ( i − 1 ) m − j ) = ∑ j = 0 m C m j S n m − j


下面是代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

long long N , a[55] ;
int T , M , P = 1e9 + 7 , C[55][55] ;

long long s_pow( long long x , int b ){
    long long rt = 1 ;
    while( b ){
        if( b&1 ) rt = rt * x %P ;
        x = x * x %P , b >>= 1 ;
    } return rt ;
}

long long S[55] ;
long long cal( long long n , int m ){
    S[0] = n + 1 ; // 1 to n
    S[1] = ( 1 + n ) * n %P * s_pow( 2 , P - 2 )%P ;
    for( int i = 2 ; i <= m ; i ++ ){
        long long tmp = s_pow( n + 1 , i + 1 ) ;
        for( int j = 2 ; j <= i + 1 ; j ++ )
            tmp -= S[i-j+1] * C[i+1][j] %P ;
        tmp = tmp %P * s_pow( i + 1 , P - 2 ) %P ;
        S[i] = ( tmp + P )%P ;
    } return S[m] ;
}

void solve(){
    long long ans = 0 ;
    for( int i = 1 ; i <= M + 1 ; i ++ ){
        ans += cal( ( N - a[i-1] )%P , M + 1 ) ;
        for( int j = i ; j <= M ; j ++ )
            ans -= s_pow( a[j] - a[i-1] , M + 1 ) ;
    } printf( "%lld\n" , ( ans %P + P )%P ) ;
}

int main(){
    for( int i = 0 ; i <= 52 ; i ++ ){
        C[i][0] = 1 ;
        for( int j = 1 ; j <= i ; j ++ )
            C[i][j] = ( C[i-1][j] + C[i-1][j-1] )%P ;
    } scanf( "%d" , &T ) ;
    while( T -- ){
        scanf( "%lld%d" , &N , &M ) ;
        for( int i = 1 ; i <= M ; i ++ )
            scanf( "%lld" , &a[i] ) ;
        sort( a + 1 , a + M + 1 ) ;
        solve() ;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值