JZOJ 5746 和

Description

给定 m m k,共 T T 次询问,每次询问一个n,输出 ni=1ik mod m ∑ i = 1 n i k   m o d   m
数据保证 m m 的最大值质因子不超过3105

Data Constraints

2n,m,k1018 1T3103 2 ≤ n , m , k ≤ 10 18   1 ≤ T ≤ 3 ∗ 10 3

Solution

首先这题用到了中国剩余定理,将 m m 分解质因数,分解成m=Πi=1vpiai的形式。
那对于某一询问,我们只需求出对于每个 i[1,v] i ∈ [ 1 , v ] ,求出 nj=1jk mod paii ∑ j = 1 n j k   m o d   p i a i ,最后用中国剩余定理组合起来即可。

现在考虑怎么求 nj=1jk mod paii ∑ j = 1 n j k   m o d   p i a i ,可以观察到 ai a i 不会超过 59 59 我们可以将每一个数拆成 xpi+y x p i + y 的形式,于是我们有

j=1njk mod paii=x0y<pi[xpi+yn](xpi+y)k mod paii ∑ j = 1 n j k   m o d   p i a i = ∑ x ∑ 0 ≤ y < p i [ x ∗ p i + y ≤ n ] ( x p i + y ) k   m o d   p i a i

为了方便,我们将答案写成
j=1n(xjpi+yj)k mod paii ∑ j = 1 n ( x j p i + y j ) k   m o d   p i a i

我们用二次项定理将 k k 次方展开,得
j=1nv=0kCkv(xjpi)vyjkv mod piai

可以看到,当 vai v ≥ a i 时,值在模意义下就变成 0 0 了,因此我们又能把式子写成
j=1nv=0min(ai,k)Ckv(xjpi)vyjkv mod piai

对于每个可能的 v v 的取值分开求解,同时预处理出前缀和数组H,
Hi,u,v=j=1ujkv mod paii(upi) H i , u , v = ∑ j = 1 u j k − v   m o d   p i a i ( u ≤ p i )

这样我们就能继续化简式子,得
v=0min(ai,k)Cvk[(npipi)vHi,(n1) mod pi+1,v+Hi, pi,vpvij=0npi1jv] ∑ v = 0 m i n ( a i , k ) C k v [ ( ⌊ n p i ⌋ p i ) v H i , ( n − 1 )   m o d   p i + 1 , v + H i ,   p i , v p i v ∑ j = 0 ⌊ n p i ⌋ − 1 j v ]

上式中 Cvk mod paii C k v   m o d   p i a i 可以通过预处理出 Fv=Cvk mod m F v = C k v   m o d   m ,再用 Fv mod paii F v   m o d   p i a i 得到。

至于 npi1j=0jv ∑ j = 0 ⌊ n p i ⌋ − 1 j v ,考虑 v v 并不会超过59,所以可以用第二类斯特林数在 O(v2) O ( v 2 ) 的时间复杂度内求解自然数幂和。

乘法要用快速乘。

时间复杂度我没去算(本蒟蒻太懒了),反正是 O( O ( 能过 ) ) <script type="math/tex" id="MathJax-Element-3993">)</script>。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#pragma GCC optimize(3)

#define fo(i,j,l) for(int i=j;i<=l;++i)
#define fd(i,j,l) for(int i=j;i>=l;--i)

using namespace std;
typedef long long ll;
const ll N=12e6,K=1e3,sj=1e7,NN=1000,P=12e2,W=3e5;

ll x[N],oo,ss[NN],gg[NN];
int t,gs[NN];
ll n,m,k;
ll kt[120][305000];
int be[K],en[K];

ll S[P][P],c[P];
ll jj[P];

inline ll mod(ll x,ll y,ll m)
{
    ll tmp=(ll)((long double)x*y/m+1e-8)*m;
    return (x*y-tmp+m)%m;
}

inline int min(int a,ll b)
{return a<b?a:b;}

inline ll mmo(ll p,ll mo)
{return (p>=mo)?p-mo:p;}

inline ll mmo_m(ll p)
{return (p>=0)?p:(p+m);}

inline ll ksm(ll o,ll t,ll m)
{
    ll y=1; o%=m;
    for(;t;t>>=1,o=mod(o,o,m))
    if(t&1)y=mod(y,o,m);
    return y;
}

inline ll ksm_mod(ll o,ll t,ll mo)
{
    ll y=1;
    for(;t;t>>=1,o=o*o%mo)
    if(t&1)y=y*o%mo;
    return y;
}

inline ll iabs(ll o)
{return (o>0)?o:(-o);}

inline ll gcd(ll a,ll b)
{return (b==0)?a:gcd(b,a%b);}

inline void prepare()
{
    S[1][1]=1; S[1][0]=0;
    fo(i,2,K){
        S[i][1]=1; S[i][0]=0;
        fo(l,2,i)S[i][l]=mmo(S[i-1][l-1]+mod(S[i-1][l],l,m),m);
    }
}

inline ll js(ll p,int k,ll m)
{
    if(k==0)return (p+1)%m;
    ll ans=0;
    fo(i,1,k){
        ll lj=1;
        fo(l,-1,i-1)if((p-l)%(i+1)==0)lj=mod(lj,(p-l)/(i+1),m);
        else lj=mod(lj,(p-l),m);
        ans=mmo(ans+mod(S[k][i],lj,m),m);
    }
    return ans;
}

inline ll C(ll a,ll b,ll mo)
{
    fo(i,1,b)jj[i]=a-i+1;
    fo(i,2,b){
        ll dq=i;
        fo(i,1,b)if(gcd(jj[i],dq)!=1){
            ll up=gcd(jj[i],dq);
            dq/=up;
            jj[i]/=up;
            if(dq==1)break;
        }
    }
    ll op=1;
    fo(i,1,b)op=mod(op,jj[i],mo);
    return op;
}

inline ll countt(ll x,ll len,int gg,int bh)
{
    ll num=(x/len)%len;
    ll mo=1,op=0;
    fo(i,1,gg)mo=mo*len;
    ll kk=(x/len); ll w=1;
    fo(i,0,min(gg,k))
    {
        ll lj=mod(kt[be[bh]+i][x%len],w,mo);
        lj=mod(lj,c[i]%mo,mo);
        op=mmo(op+lj,mo);
        w=mod(w,len,mo);
        w=mod(w,kk,mo);
    }
    w=1;
    if(kk)
    fo(i,0,min(gg,k)){
        ll lj=js(kk-1,i,mo);
        lj=mod(lj,w,mo);
        lj=mod(lj,kt[be[bh]+i][len],mo);
        lj=mod(lj,c[i]%mo,mo);
        op=mmo(op+lj,mo);
        w=mod(w,len,mo);
    }
    return op;
}

int main()
{
    cin>>m>>k>>t;
    ll op=m; prepare();
    fo(i,0,60)c[i]=C(k,i,m);
    fo(i,2,W)if(op%i==0){
        ss[++oo]=i;
        while(op%i==0)++gs[oo],op/=i;
    }
    sort(ss+1,ss+oo+1); int ok=1;
    fo(i,1,oo){
        ll mo=1;
        be[i]=en[i-1]+1; en[i]=be[i]+gs[i];
        fo(l,1,gs[i])mo=mo*ss[i];
        fo(j,0,min(gs[i],k))
        fo(l,1,ss[i])
        kt[j+be[i]][l]=mmo(kt[j+be[i]][l-1]+ksm(l,k-j,mo),mo);
    }
    fo(i,1,t){
        scanf("%lld",&n);
        ll ans=0;
        fo(i,1,oo){
            ll p=countt(n,ss[i],gs[i],i);
            ll p2=m,mo=1;
            fo(j,1,gs[i])mo=mo*ss[i],p2=p2/ss[i];
            ll p1=ksm(p2%mo,(mo/ss[i])*(ss[i]-1)-1,mo);
            p=mod(p,p1,m); p=mod(p,p2,m);
            ans=mmo(ans+p,m);
        }
        printf("%lld\n",ans);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值