jzoj4051 【SDOI2015第1轮第1试】序列统计 (用原根转离散对数,乘法转换成加法做卷积)

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

对于全部的数据,1<=N<=109,3<=M<=8000,M为质数,1<=x<=M-1,输入数据保证集合S中元素不重复。

原根,离散对数

设朴素状态 F i , j > F i + 1 , j x mod m
乘法不会搞,考虑取离散对数:
原根的定义:
模数p的原根g满足:
在模意义下
对于任意在[1,p-1]区间中的互异i,j,都有 g i g j ,显然等价于 g i = 1 当且仅当i=p-1.

也就是说 g 1 , g 2 , g 3 . . . g p 1 1 , 2 , 3... m 1 形成了一一映射。
g x = r ,则定义r的离散对数: i n d ( r ) = x
显然a*b等价于 g i n d ( a ) + i n d ( b )
这样就完成了转乘法为加法的壮举。

然后对朴素状态优化就行。其实就是对一个多项式求幂,NTT加速。
有一个坑点:输入可能有0,0没有离散对数。

如何求原根:
对于奇素数p有一个较快求法,若对于p-1的任意质因子px,都有 g i p x 1 ,则i是p的原根。
证明:
不漏:根据费马小定理 g p 1 = 1 ,因此若存在=1的就重复了,必然不是原根。
不多:即为要验证是否 g i = 1 当且仅当i=p-1.
假设存在一个 g i = 1 ,且i!=p-1。由费马小定理,i是p-1的约数。
也就是说他比p-1缺少了某些因数。这样每次去掉一个质因子,这样的i必然会被某一次的i/px包含。
包含时,有 g i p x = 1 。因此只需要验证是否

若对于p-1的任意质因子px,都有 g i p x 1 ,则i是p的原根。

验证一个数的复杂度是质因子也就是log级别的(实际更小),由于原根一般不大,所以从1开始枚举检验即可。

#include <cstdio>
#include <iostream>
#include <cstring>

using namespace std;
typedef long long ll;
const int mo=1004535809,G=3,M0=8010*2*2;
int n,m,x,S,g;
int fc[1000];
int lo[10000];
ll C[M0];

int ksm(ll x,int y,int mo) {
    ll ret=1;
    for (; y!=0; y>>=1) {
        if (y&1) ret=ret*x%mo;
        x=(ll)x*x%mo;
    }
    return ret;
}

int getroot(int f) {
    int w=f-1;
    for (int i=2; w!=1; i++) if (w%i==0) {
        fc[++fc[0]]=i;
        while (w%i==0) w/=i;
    }
    for (int i=1; i<f; i++) {
        int flag=1;
        for (int j=1; j<=fc[0]; j++) if (ksm(i,(f-1)/fc[j],f)==1) {
            flag=0; break;
        }
        if (flag) return i;
    }
}

ll D[M0],tA[M0];
ll h[M0],M,eps[M0],ieps[M0];
void dft(ll *A,int sig) {
    for (int i=0; i<M; i++) if (h[i]<i) swap(A[h[i]],A[i]);
    for (int m=2; m<=M; m*=2) {
        int hf=m/2,zg=(sig==-1)?ieps[m]:eps[m];
        for (ll i=0,z=1; i<hf; i++,z=z*zg%mo)
            for (int j=i; j<M; j+=m) {
                int u=A[j],v=A[j+hf]*z%mo;
                A[j]=(u+v)%mo,A[j+hf]=(u-v+mo)%mo;
            }
    }
    if (sig==-1) {
        int iv=ksm(M,mo-2,mo);
        for (int i=0; i<M; i++) A[i]=A[i]*iv%mo;
    }
}

void fft(ll *A,ll *B) {
    dft(A,1);
    for (int i=0; i<M; i++) A[i]=A[i]*B[i]%mo;
    dft(A,-1);
    for (int i=m; i<M; i++) 
        A[i-(m-1)]=(A[i-(m-1)]+A[i])%mo,A[i]=0;
}

void vec_ksm(ll *A,int y) {
    D[0]=1;
    for (int z=1,len=0; y!=0; y>>=1) {
        memcpy(tA,A,M*sizeof A[0]);
        dft(tA,1);
        if (y&1) fft(D,tA);
        fft(A,tA);
    }
    memcpy(A,D,sizeof D);
}

int main() {
    freopen("4051.in","r",stdin);
    cin>>n>>m>>x>>S;
    g=getroot(m);
    for (int z=1,hh=g; z<m; z++,hh=hh*g%m) lo[hh]=z;

    for (int i=1; i<=S; i++) {
        int t; scanf("%d",&t); C[lo[t%m]]++;
    }

    C[0]=0;
    for (M=1; M<m*2; M*=2);
    for (int i=1; i<M; i++) h[i]=(h[i>>1]>>1) + (M>>1) * (i&1);
    for (int i=1; i<=M; i*=2) eps[i]=ksm(G,(mo-1)/i,mo),ieps[i]=ksm(eps[i],mo-2,mo);

    vec_ksm(C,n);
    cout<<C[lo[x]]<<endl;
}

猜你喜欢

转载自blog.csdn.net/jokerwyt/article/details/80456871