loj6059Sum

Posted yoyoball

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了loj6059Sum相关的知识,希望对你有一定的参考价值。

Portal --> loj6059

Solution

??  看过去第一反应是。。大力数位dp!然后看了一眼数据范围。。。

?  但是这没有什么关系!注意到我们不需要考虑前导零了,可以直接快乐dp

?  状态还是能继续用的,记(f[i][j][k])表示从左往右数的前(i)位,(假装后面没有数位的情况下)模(p)(j),数字和为(k)

?  然后。。(n)特别大所以我们考虑。。倍增求解,考虑从(lfloorfrac{i}{2} floor)转移到(i)
[ f[lfloorfrac{i}{2} floor][x][j]*f[lfloorfrac{i}{2} floor][y][k] ightarrow f[i][x+y][(j+10^w*k)\\%p] ]
?  这个(w)的话就是。。(lfloorfrac{i}{2} floor)

??  但是如果说(i)是奇数怎么办呢?其实只要在这样转移完了之后再暴力枚举一下最高位是啥就好了(现在是相当于得到了一个(i-1)位的数嘛)

??  然后发现因为(p)(m)都比较小,所以我们可以直接枚举,而第二维的那个(f[][x][j]*f[][y][k] ightarrow f[][x+y][])的是一个卷积的形式,我们可以用NTT来优化

??  具体的话其实感觉跟这题的处理有点像【Portal -->Lcm】,也是我们先将(f[i][x])DFT(NTT)一下之后就可以随便搞事了,也就是相当于第二维和第三维在某种意义上独立了,然后我们可以将转移分开处理(先搞第二维的转移,再暴力枚举第三维的转移)

?  至于倍增的话。。递归就好了,边界的话就是(i=0)的情况

??  因为中间要快乐NTT所以一定要记得相关数组清空

?  

??  代码大概长这个样子

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int MOD=998244353,N=3010,M=3010,NT=N*4,TOP=11,G=3;
int f[N][M],g[N][M];
int n,m,p,ans;
int mul(int x,int y){return 1LL*x*y%MOD;}
int add(int x,int y){return (1LL*x+y)%MOD;}
int ksm(int x,int y){
    int ret=1,base=x;
    for (;y;y>>=1,base=mul(base,base))
        if (y&1) ret=mul(ret,base);
    return ret;
}
namespace NTT{/*{{{*/
    int A[NT],B[NT],W[NT][2],rev[NT];
    int len,invlen,invg;
    void get_len(int n,int m){
        for (int i=0;i<len;++i) A[i]=B[i]=0;
        int bit=0;
        for (len=1;len<=n+m;len<<=1,++bit);
        rev[0]=0;
        for (int i=1;i<len;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
        invlen=ksm(len,MOD-2);
    }
    void init(int n){
        invg=ksm(G,MOD-2);
        for (int i=1;i<=TOP;++i){
            W[1<<i][0]=ksm(G,(MOD-1)/(1<<i));
            W[1<<i][1]=ksm(invg,(MOD-1)/(1<<i));
        }
        get_len(n,n);
    }
    void ntt(int *a,int op){
        int w,w_n,u,v;
        for (int i=0;i<len;++i) if (rev[i]>i) swap(a[i],a[rev[i]]);
        for (int step=2;step<=len;step<<=1){
            w_n=W[step][op==-1];
            for (int st=0;st<len;st+=step){
                w=1;
                for (int i=0;i<(step>>1);++i){
                    v=mul(a[st+i+(step>>1)],w);
                    u=a[st+i];
                    a[st+i]=add(u,v);
                    a[st+i+(step>>1)]=add(u,MOD-v);
                    w=mul(w,w_n);
                }
            }
        }
        if (op==1) return;
        for (int i=0;i<len;++i) a[i]=mul(a[i],invlen);
    }
}/*}}}*/
int work(int n){
    if (!n) return 1;
    int mi=work(n>>1);
    for (int i=0;i<p;++i) 
        NTT::ntt(g[i],1);
    for (int i=0;i<p;++i)
        for (int j=0;j<p;++j)
            for (int k=0;k<NTT::len;++k)
                f[(i+j*mi%p)%p][k]=add(f[(i+j*mi%p)%p][k],mul(g[i][k],g[j][k]));
    for (int i=0;i<p;++i) 
        for (int j=0;j<NTT::len;++j)
            g[i][j]=0;
    for (int i=0;i<p;++i){
        NTT::ntt(f[i],-1);
        for (int j=0;j<m;++j) g[i][j]=f[i][j];
        for (int j=0;j<NTT::len;++j) f[i][j]=0;
    }
    mi=mi*mi%p;
    if (n&1){
        for (int i=0;i<p;++i)
            for (int x=0;x<10;++x)
                for (int j=0;j+x<m;++j)
                    f[(i+x*mi%p)%p][j+x]=add(f[(i+x*mi%p)%p][j+x],g[i][j]);
        for (int i=0;i<p;++i)
            for (int j=0;j<m;++j)
                g[i][j]=f[i][j],f[i][j]=0;
        mi=mi*10%p;
    }
    return mi;
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("a.in","r",stdin);
#endif
    scanf("%d%d%d",&n,&p,&m);
    g[0][0]=1; ++m;
    NTT::init(m);
    work(n);
    ans=0;
    for (int i=0;i<m;++i){
        ans=add(ans,g[0][i]);
        printf("%d ",ans);
    }
}

以上是关于loj6059Sum的主要内容,如果未能解决你的问题,请参考以下文章

LOJ132. 树状数组 3 :区间修改,区间查询 题解

loj6485 LJJ 学二项式定理

Loj #6703 -「清华集训 2017」生成树计数

[loj2473]秘密袭击

Loj 6433. 「PKUSC2018」最大前缀和 (状压dp)

507 LOJ