小学生数学题题解

Description:

ans=i=1n1imodpk

Solution:


f(x,y)=i=1x1imodpy

因为要i与p互质才有逆元,所以要分类:

Part 1——i不与p互质

因为p是质数,那么这种可能只有i是p的倍数。
那么我们对这些数求和。

i=1np1ipmodpk

把所有的1/p提取出来

1pi=1np1imodpk

因为还要除以一个p,我们想办法把它消掉。
假如 amodb=c ,那么就是 akmodbk=ck
那么上式可以化简为

npi=11imodpk+1p

把类似f(x,y)的带进去

f(np,k+1)p

然后f一直递归下去
递归出口x=0时return 0

Part2——i不与p互质

如果i与p互质,那么所有的数就是
11 12 …… 1p1
.
.
.
.
.
.
1p(np1) 1p(np1)+1 …… 1p(np)1
还剩下一些数,用暴力求出它的和

i=pnpn1i

但是上面那些数我们要怎么求呢?

i=1p1a=0np11ap+i

如果你知识量够广的话,如果你知道泰勒展开式的话,那就好解决了,不用百度,往下看。
因为

11x=x0+x1++x(0<=x<1)

证明:
上面明显是等比数列,那么和为

x1x1=11x(0<=x<1x=0)

有了上面的结论之后,式子要是分母含有1和东西
那么分子分母同时乘上 i1

i=1p1a=0np1i1api1+1

i1 带出来

i=1p1i1a=0np11api1+1

那么我们设 x=i1ap
那么根据泰勒展开式就会变为

i=1p1i1a=0np1b=0xb

把x还原,那么就是

i=1p1i1a=0np1b=0(i1apb)

但是有∞,那么怎么算呢?
我们发现答案还是要mod pk
当b=k时,就会有 i1apk ,这个mod pk 是为0的,这个和后面的式子明显都有 pk 这个因数,所以并不需要统计,因为都是+0,+0,+0……
那么我们的答案就是

i=1p1i1a=0np1b=0k1(i1apb)

我们发现这样很难统计,我们根据乘法分配律把 外置出来

i=1p1i1b=0k1i1pba=0np1ab

那么我们在加上剩下的值,就是这一段part2的答案
注意逆元用扩展欧几里得
求自然数幂和的方法,请转移自然数幂求和的各种方法

Code

我打了个伯努利数,第三个样例过不了,OJ 100分。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define ll long long
using namespace std;
const int maxn=100007;
ll c[200][200],s[maxn];
ll i,j,k,l,t,n,m,ans,p,mo;
void exgcd(ll a,ll b,ll &x,ll &y){
    if(b==0){
        x=1,y=0;
        return; 
    }
    ll xx,yy;
    exgcd(b,a%b,xx,yy);
    x=yy;
    y=xx-a/b*yy;
}
ll qsm(ll x,ll y){
    ll z=1;
    for(;y;y>>=1){
        if(y&1)z=(z*x)%mo;
        x=(x*x)%mo;
    }
    return z;
} 
ll niyuan(ll yi){
    ll x,y,z;
    exgcd(yi,mo,x,y);
    z=(x%mo+mo)%mo;
    return z;
}
ll strling(ll x,ll y){
    ll z=0,i;
    if(x<0){
        fo(i,0,x){
            z=(z+qsm(i,y))%mo;
        }
        return z;
    }
    s[0]=1;
    fo(i,1,y){
        ll o=-niyuan(i+1);
        ll d=0;
        fo(j,0,i-1){
            d=(d+c[i+1][j]*s[i]%mo+mo)%mo;
        }
        o=(o*d)%mo;
        s[i]=o;
    }
    return s[y];
}
ll compute(ll x,ll ci){
    if(x==0)return 0;
    ll an=0,b,i;
    fo(i,1,ci-1) c[i][0]=c[0][i]=1;
    fo(i,1,ci-1){
        fo(j,1,i){
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%mo;
        }
    }
    c[0][0]=1;
    fo(i,1,p-1){
        ll o=niyuan(i);
        fo(b,0,ci-1){
            an=(an+o*qsm(-o*p%mo,b)%mo*strling(x/p-1,b)%mo)%mo;    
        }
    }
    fo(i,x/p*p+1,x){
        ll o=niyuan(i);
        an=(an+o)%mo;
    }
    ll moo=mo;
    mo=mo*p;
    an=(an+compute(x/p,ci+1)/p)%moo;
    an=(an%moo+moo)%moo;
    return an;
}
int main(){
    scanf("%lld%lld%lld",&p,&k,&n);
    mo=1;
    fo(i,1,k)mo=mo*p;
    printf("%lld\n",compute(n,k));
}

我打了个差分表,第三个样例过不了,OJ 100分。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define ll long long
using namespace std;
const int maxn=100007;
ll c[200][200],s[maxn];
ll i,j,k,l,t,n,m,ans,p,mo;
void exgcd(ll a,ll b,ll &x,ll &y){
    if(b==0){
        x=1,y=0;
        return; 
    }
    ll xx,yy;
    exgcd(b,a%b,xx,yy);
    x=yy;
    y=xx-a/b*yy;
}
ll qsm(ll x,ll y){
    ll z=1;
    for(;y;y>>=1){
        if(y&1)z=(z*x)%mo;
        x=(x*x)%mo;
    }
    return z;
} 
ll niyuan(ll yi){
    ll x,y,z;
    exgcd(yi,mo,x,y);
    z=(x%mo+mo)%mo;
    return z;
}
ll strling(ll x,ll y){
    ll z=0,i;
    if(x<0){
        fo(i,0,x){
            z=(z+qsm(i,y))%mo;
        }
        return z;
    }
    s[0]=x+1;
    ll o=niyuan(2);
    s[1]=x*(x+1)%mo*o%mo;
    fo(i,2,y){
        ll o=niyuan(i+1);
        ll d=(qsm(x+1,i+1)-x-1+mo)%mo;
        fo(j,2,i){
            d=(d-c[i+1][j]*s[i-j+1]%mo+mo)%mo;
        }
        d=(d+mo)%mo;
        o=(o*d)%mo;
        s[i]=o;
    }
    return s[y];
}
ll compute(ll x,ll ci){
    if(x==0)return 0;
    ll an=0,b,i;
    fo(i,1,ci-1) c[i][0]=c[0][i]=1;
    fo(i,1,ci-1){
        fo(j,1,i){
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%mo;
        }
    }
    c[0][0]=1;
    fo(i,1,p-1){
        ll o=niyuan(i);
        fo(b,0,ci-1){
            an=(an+o*qsm(-o*p%mo,b)%mo*strling(x/p-1,b)%mo)%mo;    
        }
    }
    fo(i,x/p*p+1,x){
        ll o=niyuan(i);
        an=(an+o)%mo;
    }
    ll moo=mo;
    mo=mo*p;
    an=(an+compute(x/p,ci+1)/p)%moo;
    an=(an%moo+moo)%moo;
    return an;
}
int main(){
    scanf("%lld%lld%lld",&p,&k,&n);
    mo=1;
    fo(i,1,k)mo=mo*p;
    printf("%lld\n",compute(n,k));
}

但是用第一类斯特林数可以过第三个点,也可以100分
Werkey_Tom的

ll get(ll n,ll k){
    su[0][0]=1;
    ll i,j;
    fo(i,1,k) su[i][0]=0,su[i][i]=1;
    fo(i,1,k)
        fo(j,1,i-1)
            su[i][j]=(su[i-1][j-1]+(i-1)*su[i-1][j]%m)%m;
    if (!n) return 0;
    if (n<=k){  
        t=0;
        fo(i,0,n){
            l=1;
            fo(j,1,k)
                l=l*i%m;
            t=(t+l)%m;
        }
        return t;
    }
    fo(i,0,k) f[i]=0;f[0]=(n+1)%m;
    if (n%2==0) f[1]=n/2*(n+1)%m;
    else f[1]=n*(n+1)/2%m;
    fo(i,2,k){
        t=1;
        fo(j,n+1-i,n+1)
            if (j%(i+1)==0) t=t*j/(i+1)%m;else t=t*j%m;
        f[i]=t;
        fo(j,0,i-1){
            if ((j+i)%2==0) l=1;else l=-1;
            f[i]=((f[i]-l*su[i][j]%m*f[j]%m)%m+m)%m;
        }
        f[i]=f[i]%m;
    }
    return f[k];
}
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值