• LOJ #2183「SDOI2015」序列统计


    有好多好玩的知识点

    LOJ

    题意:在集合中选$ n$个元素(可重复选)使得乘积模$ m$为$ x$,求方案数对$ 1004535809$取模

    $ n<=10^9,m<=8000且是质数,集合大小不超过m$


    $ Solution:$

    我们先考虑改乘积为加和之后怎么做

    直接对于集合中的数构建生成函数

    所要求的就是这个生成函数的$ n$次幂的所有模$ m$为$ c$的项的系数的和

    用快速幂优化这个生成函数的$ n$次幂

    每次乘法之后立刻把$ [m,2m)$的系数加回$[0,m)$

    这样可以保证每时每刻生成函数的长度不超过$ m$

    就可以直接$ NTT$优化这个过程了

    时间复杂度为$ O(m log m log n)$

    然后考虑乘积的情况

    我们知道$ x^ax^b=x^{a+b}$

    尝试把每个数改成某个底数的若干次方

    由于$ m$是质数一定存在原根

    原根有性质是它的$[0,phi(m))$在模$ m$意义下互不相同

    这样就可以直接把每个集合中的数改成$ m$的原根的若干次方就好了

    然后就是加和情况的做法

    复杂度不变

    注意可能要特判原集合中存在$ 0$的情况


     $ my code$

    #include<ctime>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #define p 1004535809
    #define rt register int
    #define ll long long
    using namespace std;
    inline ll read(){
        ll x = 0; char zf = 1; char ch = getchar();
        while (ch != '-' && !isdigit(ch)) ch = getchar();
        if (ch == '-') zf = -1, ch = getchar();
        while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x * zf;
    }
    void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);}
    void writeln(const ll y){write(y);putchar('
    ');}
    int i,j,k,m,n,x,y,z,cnt,c,yg;
    int a[100010],val[8555];
    namespace NTT{
        int ksm(int x,int y){
            int ans=1;
            for(rt i=y;i;i>>=1,x=1ll*x*x%p)if(i&1)ans=1ll*x*ans%p;
            return ans;
        }
        vector<int>R;
        void NTT(int n,vector<int>&A,int fla){
            A.resize(n);    
            for(rt i=0;i<n;i++)if(i>R[i])swap(A[i],A[R[i]]);
            for(rt i=1;i<n;i<<=1){
                int w=ksm(3,(p-1)/2/i);
                for(rt j=0;j<n;j+=i<<1){
                    int K=1;
                    for(rt k=0;k<i;k++,K=1ll*K*w%p){
                        const int x=A[j+k],y=1ll*K*A[i+j+k]%p;
                        A[j+k]=(x+y)%p,A[i+j+k]=(x-y)%p;
                    }
                }
            }
            if(fla==-1){
                reverse(A.begin()+1,A.end());
                int invn=ksm(n,p-2);
                for(rt i=0;i<n;i++)A[i]=1ll*A[i]*invn%p;
            }
        }
        void mul(int del,int n,vector<int>&A){
            NTT(n,A,1);
            for(rt i=0;i<n;i++)A[i]=1ll*A[i]*A[i]%p;
            NTT(n,A,-1);
            for(rt i=del;i<n;i++)(A[i%del]+=A[i])%=p,A[i]=0;
        }
        void calc(int n,vector<int>&A,int y,int pl){
            int lim=1;
            while(lim<=n*2+2)lim<<=1;
            R.resize(lim);A.resize(lim);
            for(rt i=1;i<lim;i++)R[i]=(R[i>>1]>>1)|((i&1)*(lim>>1));
            vector<int>ans;ans.resize(lim);
            ans[0]=1;
            for(rt i=y;i;i>>=1,mul(n,lim,A))if(i&1){
                NTT(lim,ans,1);NTT(lim,A,1);
                for(rt j=0;j<lim;j++)ans[j]=1ll*ans[j]*A[j]%p;
                NTT(lim,ans,-1);NTT(lim,A,-1);
                for(rt j=n;j<lim;j++)(ans[j%n]+=ans[j])%=p,(A[j%n]+=A[j])%=p,ans[j]=0,A[j]=0;
            }
            writeln((ans[pl]+p)%p);
        }
    };
    vector<int>A,B;
    using namespace NTT;
    int main(){
        n=read();m=read();c=read();k=read();
        A.resize(m+1);
        for(rt i=2;i<=m;i++){
            for(rt j=1,k=i;j<m-1;j++,k=1ll*k*i%m)if(k==1)goto GG;
            yg=i;break;
            GG:;
        }
        for(rt i=0,j=1;i<m-1;i++,j=1ll*yg*j%m)val[j]=i;
        for(rt i=1;i<=k;i++){
            x=read();if(x)A[val[x]]++;
        }
        calc(m-1,A,n,val[c]);
        return 0;
    }
  • 相关阅读:
    ES集群性能调优链接汇总
    【转】dmesg 时间转换
    广师大笔记汉诺塔
    广师大python学习笔记求派的值
    155. 最小栈(c++)
    160. 相交链表(c++)
    论文 数据集总结
    论文阅读 总结 复习
    121. 买卖股票的最佳时机(c++)
    9. 回文数(c++)
  • 原文地址:https://www.cnblogs.com/DreamlessDreams/p/10036133.html
Copyright © 2020-2023  润新知