【GDKOI2016】小学生数学题 【自然数幂求和】【斯特林数】

题意:求 ni=1i1 mod pk ∑ i = 1 n i − 1   m o d   p k 的值。保证 p p 为质数,n×pk1018
题解:神题!细节巨多!
f(n,k)=ni=1i1 mod pk f ( n , k ) = ∑ i = 1 n i − 1   m o d   p k g(n,k)=ni=1,ijpi1 mod pk g ( n , k ) = ∑ i = 1 , i ≠ j p n i − 1   m o d   p k
f(n,k)=g(n,k)+f(np,k+1)p f ( n , k ) = g ( n , k ) + f ( ⌊ n p ⌋ , k + 1 ) p
为什么后面那里是k+1呢?
因为若 ab mod c a ≡ b   m o d   c ,则 akbk mod ck a k ≡ b k   m o d   c k
我们把那一部分先整体乘 p p ,最后再除回来就好。
这样我们就可以递归计算了。
如何求g(n,k)?
g(n,k)
=i=a+bp<=n1i = ∑ i = a + b p <= n 1 i
=p1a=1napb=01a+bp = ∑ a = 1 p − 1 ∑ b = 0 ⌊ n − a p ⌋ 1 a + b p
=p1a=1napb=0(a+bp)1 = ∑ a = 1 p − 1 ∑ b = 0 ⌊ n − a p ⌋ ( a + b p ) − 1
这个东西用广义二项式定理展开一下
(a+bp)1=i=0Ci1a1ibipi=i=0(1)ia1ibipi ( a + b p ) − 1 = ∑ i = 0 ∞ C − 1 i a − 1 − i b i p i = ∑ i = 0 ∞ ( − 1 ) i a − 1 − i b i p i
又因为我们模了一个 pk p k
所以
(a+bp)1=k1i=0(1)ia1ibipi ( a + b p ) − 1 = ∑ i = 0 k − 1 ( − 1 ) i a − 1 − i b i p i
所以
g(n,k) g ( n , k )
=k1i=0(p)ip1a=11ai+1napb=0bi = ∑ i = 0 k − 1 ( − p ) i ∑ a = 1 p − 1 1 a i + 1 ∑ b = 0 ⌊ n − a p ⌋ b i
我们令 Sk(n) S k ( n ) 表示 ni=0ik ∑ i = 0 n i k
g(n,k)=k1i=0(p)ip1a=11ai+1Si(nap) g ( n , k ) = ∑ i = 0 k − 1 ( − p ) i ∑ a = 1 p − 1 1 a i + 1 S i ( ⌊ n − a p ⌋ )
如何求 Sk(n)? S k ( n ) ?
我们先证一个东西:
xn¯¯¯=Πx+n1i=xi=ks(n,k)xk x n ¯ = Π i = x x + n − 1 i = ∑ k s ( n , k ) x k ,s是第一类斯特林数。
可以转化为证 [xp]xn¯¯¯=[xp]ks(n,k)xk=s(n,p) [ x p ] x n ¯ = [ x p ] ∑ k s ( n , k ) x k = s ( n , p )
显然在 n=1 n = 1 的情况下是成立的。
[xk]xn¯¯¯ [ x k ] x n ¯
=[xk](x+n1)xn1¯¯¯¯¯¯¯¯¯¯ = [ x k ] ( x + n − 1 ) x n − 1 ¯
=[xk](x+n1)xn1¯¯¯¯¯¯¯¯¯¯ = [ x k ] ( x + n − 1 ) x n − 1 ¯
=[xk]x×xn1¯¯¯¯¯¯¯¯¯¯+[xk](n1)xn1¯¯¯¯¯¯¯¯¯¯ = [ x k ] x × x n − 1 ¯ + [ x k ] ( n − 1 ) x n − 1 ¯
=[xk1]xn1¯¯¯¯¯¯¯¯¯¯+[xk](n1)xn1¯¯¯¯¯¯¯¯¯¯ = [ x k − 1 ] x n − 1 ¯ + [ x k ] ( n − 1 ) x n − 1 ¯
=s(n1,k1)+(n1)s(n1,k) = s ( n − 1 , k − 1 ) + ( n − 1 ) s ( n − 1 , k )
=s(n,k) = s ( n , k )
证明完毕。
我们再证一个东西:

xn=n!Cnxj=1n1s(n,j)xj x n = n ! C x n − ∑ j = 1 n − 1 s ( n , j ) x j

其实挺显然的,上面那个证明的式子 xn x n 的项系数为1,所以一减就可以得到这个式子。
我们还要证一个东西:
i=knCki=Ck+1n+1 ∑ i = k n C i k = C n + 1 k + 1

怎么证?
i=knCki=i=kn1Cki+Ckn=Ckn+Ck+1n=Ck+1n+1 ∑ i = k n C i k = ∑ i = k n − 1 C i k + C n k = C n k + C n k + 1 = C n + 1 k + 1

证明完毕。
终于要继续自然数幂求和啦!有了之前推的一堆式子,我们就可以化简式子了。
Sk(n) S k ( n )
=ni=0ik = ∑ i = 0 n i k
=ni=0k!Ckik1j=1s(k,j)ij = ∑ i = 0 n k ! C i k − ∑ j = 1 k − 1 s ( k , j ) i j
=k!(ni=0Cki)ni=0k1j=1s(k,j)ij = k ! ( ∑ i = 0 n C i k ) − ∑ i = 0 n ∑ j = 1 k − 1 s ( k , j ) i j
=k!Ck+1n+1k1j=1s(k,j)ni=0ij = k ! C n + 1 k + 1 ∑ j = 1 k − 1 s ( k , j ) ∑ i = 0 n i j
对于固定的 n n ,这个可以O(k2)预处理出来。然后代会这个式子里计算。
g(n,k)=k1i=0(p)ip1a=11ai+1Si(nap) g ( n , k ) = ∑ i = 0 k − 1 ( − p ) i ∑ a = 1 p − 1 1 a i + 1 S i ( ⌊ n − a p ⌋ )
对于一些零碎的部分,我是直接暴力计算的。
这一题数域可能很大,我写了 O(1) O ( 1 ) 快速乘法。
这题好难写啊!QAQ
代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define int long long
using namespace std;
int p,k,n,x,y,r[100005],s[105][105];
int mul(int a,int b,int mod){
    int c=a*(double)b/mod+0.5;
    int res=a*b-c*mod;
    if(res<0){
        res+=mod;
    }
    return res;
}
int fastpow(int a,int x,int mod){
    a%=mod;
    int res=1;
    while(x){
        if(x&1){
            res=mul(res,a,mod);
        }
        x>>=1;
        a=mul(a,a,mod);
    }
    return res;
}
int exgcd(int a,int b,int &x,int &y){
    if(!b){
        x=1;
        y=0;
        return a;
    }
    int d=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return d;
}
int getinv(int a,int b){
    exgcd(a,b,x,y);
    return (x%b+b)%b;
}
void init(int n,int k){
    const int mod=pow(p,k);
    r[0]=n+1;
    s[0][0]=1;
    for(int i=1;i<=k;i++){
        for(int j=1;j<=k;j++){
            s[i][j]=(s[i-1][j-1]+mul(s[i-1][j],i-1,mod))%mod;
        }
    }
    for(int i=1;i<=k;i++){
        for(int j=1;j<=i;j++){
            if((i+j)&1){
                s[i][j]=mod-s[i][j];
            }
        }
    }
    for(int i=1;i<=k;i++){
        r[i]=1;
        bool flag=false;
        for(int j=n+1;j>=n-i+1;j--){
            if(!flag&&j%(i+1)==0){
                flag=true;
                r[i]=mul(r[i],j/(i+1),mod);
            }else{
                r[i]=mul(r[i],j,mod);
            }
        }
        if(!flag){
            r[i]=mul(r[i],getinv(i+1,mod),mod);
        }
        for(int j=1;j<i;j++){
            r[i]=(r[i]-mul(s[i][j],r[j],mod)+mod)%mod;
        }
    }
    return;
}
int g(int n,int k){
    const int mod=pow(p,k);
    int rem=n%p,res=0;
    for(int i=n-rem+1;i<=n;i++){
        res=(res+getinv(i,mod))%mod;
    }
    n-=rem;
    if(!n){
        return res;
    }
    init((n-1)/p,k);
    for(int i=0,base=1;i<k;i++,base=(base*(-p)%mod+mod)%mod){
        for(int j=1;j<p;j++){
            res=(res+mul(mul(base,getinv(fastpow(j,i+1,mod),mod),mod),r[i],mod))%mod;
        }
    }
    return res;
}
int f(int n,int k){
    if(!n){
        return 0;
    }
    return (g(n,k)+f(n/p,k+1)/p)%(int)(pow(p,k));
}
signed main(){
    scanf("%lld%lld%lld",&p,&k,&n);
    printf("%lld\n",f(n,k));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值