T4 小学生数学题
求
∑i=1n1imodpk
首先,我们把原式分成两部分,一部分对于p有逆元,另一部分没有。
即原式=
1p∑i=1⌊np⌋1i+∑a=1p−1∑i=0⌊np⌋−11a+i∗pmodpk
剩余一部分,由于个数少于p,暴力做就行了。
然后分类讨论
part 1
1p∑i=1⌊np⌋1imodpk
一个明显的结论
若
amodb=c
则
akmodbk=ck
设
f(n,k)=∑i=1n1imodpk
那么,原式=
f(⌊np⌋,k+1)modpk+1p
递归下去做就好了。
part 2
∑a=1p−1∑i=0⌊np⌋−11a+i∗pmodpk
原式=
∑a=1p−1∑i=0⌊np⌋−1a−1∗11+i∗a−1∗pmodpk
链接一个泰勒展开,当 x∞=0 时
11−x=x0+x1+x2+...+x∞
所以原式可以写成
∑a=1p−1∑i=0⌊np⌋−1a−1∗∑b=0k−1(−i∗a−1∗p)bmodpk
为什么只到k-1次幂呢,因为这是在模 pk 的意义下进行的,大于等于k的话就为0了。
考虑交换枚举顺序,原式=
∑a=1p−1a−1∗∑b=0k−1(−a−1∗p)b∗∑i=0⌊np⌋−1ibmodpk
这样求一个 自然数幂和就可以解决了。
#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
ll i,j,k,l,t,n,m,a,b,p;
ll su[150][150];
ll f[150];
ll qsm(ll x,ll y){
if (!y) return 1;
ll t=qsm(x,y / 2);
t=t*t%m;
if (y%2) t=t*(x%m)%m;
return t;
}
ll get(ll n,ll k){
if (n<=0) return 0;
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;
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];
}
void gcd(ll a,ll b,ll &x,ll &y){
if (!b){
x=1;y=0;
return;
}
ll xx,yy;
gcd(b,a%b,xx,yy);
x=yy;y=xx-a/b*yy;
}
ll getny(ll a,ll b){
ll x,y;
gcd(a,b,x,y);
x=(x%b+b)%b;
return x;
}
ll solve(ll n,ll k){
if (!n) return 0;
ll ans=0,t,mo,r,i;
r=(n/p)*p;
fo(a,1,p-1){
t=getny(a,m);
fo(b,0,k-1) ans=(ans+t*qsm(-t*p,b)%m*get(n/p-1,b)%m)%m;
}
fo(i,r+1,n) ans=(ans+getny(i,m))%m;
mo=m;
m=m*p;
ans=(ans+solve(n/p,k+1)/p)%mo;
ans=(ans%mo+mo)%mo;
return ans;
}
int main(){
scanf("%lld%lld%lld",&p,&k,&n);
m=1;
fo(i,1,k) m=m*p;
printf("%lld\n",solve(n,k));
}