• BZOJ3992: [SDOI2015]序列统计


    Description

    小C有一个集合S,里面的元素都是小于M的非负整数。他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S。
    小C用这个生成器生成了许多这样的数列。但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中所有数的乘积mod M的值等于x的不同的数列的有多少个。小C认为,两个数列{Ai}和{Bi}不同,当且仅当至少存在一个整数i,满足Ai≠Bi。另外,小C认为这个问题的答案可能很大,因此他只需要你帮助他求出答案mod 1004535809的值就可以了。

    Input

    一行,四个整数,N、M、x、|S|,其中|S|为集合S中元素个数。第二行,|S|个整数,表示集合S中的所有元素。

    Output

    一行,一个整数,表示你求出的种类数mod 1004535809的值。

    Sample Input

    4 3 1 2
    1 2

    Sample Output

    8

    HINT

    【样例说明】

    可以生成的满足要求的不同的数列有(1,1,1,1)、(1,1,2,2)、(1,2,1,2)、(1,2,2,1)、(2,1,1,2)、(2,1,2,1)、(2,2,1,1)、(2,2,2,2)。

    【数据规模和约定】

    对于10%的数据,1<=N<=1000;

    对于30%的数据,3<=M<=100;

    对于60%的数据,3<=M<=800;

    对于全部的数据,1<=N<=10^9,3<=M<=8000,M为质数,1<=x<=M-1,输入数据保证集合S中元素不重复
     
    好厉害。。。
    首先有10分算法,设f[i][j]表示选i个数,乘积模M结果为x的方案数。
    然后因为M为质数,我们可以求出M的原根g,这样转移就可以写成关于g的生成函数,就可以用NTT来加速了。
    接下来发现每次乘的都是相同的多项式,那么多项式快速幂即可。
    #include<cstdio>
    #include<cctype>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define dwn(i,s,t) for(int i=s;i>=t;i--)
    #define ren for(int i=first[x];i;i=next[i])
    using namespace std;
    const int BufferSize=1<<16;
    char buffer[BufferSize],*head,*tail;
    inline char Getchar() {
        if(head==tail) {
            int l=fread(buffer,1,BufferSize,stdin);
            tail=(head=buffer)+l;
        }
        return *head++;
    }
    inline int read() {
        int x=0,f=1;char c=Getchar();
        for(;!isdigit(c);c=Getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=Getchar()) x=x*10+c-'0';
        return x*f;
    }
    const int maxn=18010;
    const int p=1004535809;
    const int G=3;
    const int NUM=15;
    typedef long long ll;
    ll wn[maxn],inv;
    ll pow(ll n,ll m,ll mod=p) {
        ll ans=1;
        for(;m;m>>=1,(n*=n)%=mod) if(m&1) (ans*=n)%=mod;
        return ans;
    }
    void NTT(ll* A,int len,int tp) {
        int j=len>>1,c=0;
        rep(i,1,len-2) {
            if(i<j) swap(A[i],A[j]);int k=len>>1;
            while(j>=k) j-=k,k>>=1;j+=k;
        }
        for(int i=2;i<=len;i<<=1) {
            c++;
            for(int j=0;j<len;j+=i) {
                ll w=1;
                for(int k=j;k<j+(i>>1);k++) {
                    ll u=A[k],t=w*A[k+(i>>1)]%p;
                    A[k]=(u+t)%p;A[k+(i>>1)]=(u-t+p)%p;
                    (w*=wn[c])%=p;
                }
            }
        }
        if(tp<0) {
            rep(i,1,len/2-1) swap(A[i],A[len-i]);
            ll inv=pow(len,p-2);
            rep(i,0,len-1) (A[i]*=inv)%=p; 
        }
    }
    int check(int g,int m) {
        for(int i=2;i*i<m;i++) if((m-1)%i==0&&(pow(g,i,m)==1||pow(g,(m-1)/i,m)==1)) return 0;
        return 1;
    }
    int n,m,X,S,len,gs=2,c[maxn];
    ll D[maxn];
    void mul(ll* A,ll* B) {
        rep(i,0,len-1) D[i]=B[i];
        NTT(A,len,1);NTT(D,len,1);
        rep(i,0,len-1) (A[i]*=D[i])%=p;
        NTT(A,len,-1);
        dwn(i,len-1,m-1) (A[i-m+1]+=A[i])%=p,A[i]=0;
    }
    ll A[maxn],B[maxn];
    int main() {
        rep(i,0,NUM-1) wn[i]=pow(G,(p-1)/(1<<i));
        n=read();m=read();X=read();S=read();
        len=1;while(len<=(m<<1)) len<<=1;
        while(!check(gs,m)) gs++;
        int m0=1;c[1]=0;
        rep(i,1,m-2) (m0*=gs)%=m,c[m0]=i;
        rep(i,1,S) {
            int x=read()%m;
            if(x) A[c[x]]=1;
        }
        B[0]=1;
        for(;n;mul(A,A),n>>=1) if(n&1) mul(B,A);
        printf("%lld
    ",B[c[X]]);
        return 0;
    }
    

      

  • 相关阅读:
    C# 把一个文件夹下所有文件复制到另一个文件夹下 把一个文件夹下所有文件删除(转)
    【总结整理】webGIS学习thinkGIS(四)WebGIS中通过行列号来换算出多种瓦片的URL 之离线地
    ARCGIS空间叠加分析(转)
    ARCGIS中怎么去除重复的面?(转)
    关于写作赚钱(转)
    【总结整理】WebGIS学习-thinkGIS(三):关于影像金字塔、瓦片行列号、分辨率resolution
    【总结整理】WebGIS学习-thinkGIS(地理常识):
    【总结整理】WebGIS学习-thinkGIS(二):关于level,比例尺scale,分辨率resolution
    【总结整理】AMAP学习AMAP.PlaceSearch()
    logging、hashlib、collections模块
  • 原文地址:https://www.cnblogs.com/wzj-is-a-juruo/p/5316461.html
Copyright © 2020-2023  润新知