【SDOI2015】序列统计 解题报告

2119: 【BZOJ3992】【SDOI2015】序列统计

Description

\(C\)有一个集合\(S\),里面的元素都是小于\(M\)的非负整数。

他用程序编写了一个数列生成器,可以生成一个长度为\(N\)的数列,数列中的每个数都属于集合\(S\)

\(C\)用这个生成器生成了许多这样的数列。但是小\(C\)有一个问题需要你的帮助:给定整数\(x\),求所有可以生成出的,且满足数列中所有数的乘积\(\bmod M\)的值等于\(x\)的不同的数列的有多少个。

\(C\)认为,两个数列\(\{A_i\}\)\(\{B_i\}\)不同,当且仅当至少存在一个整数\(i\),满足\(A_i≠B_i\)。另外,小\(C\)认为这个问题的答案可能很大,因此他只需要你帮助他求出答案\(\bmod 1004535809\)的值就可以了。

Input

一行,四个整数,\(N\)\(M\)\(x\)\(|S|\),其中\(|S|\)为集合\(S\)中元素个数。

第二行,\(|S|\)个整数,表示集合\(S\)中的所有元素。

Output

一行,一个整数,表示你求出的种类数\(\bmod 1004535809\)的值。

HINT

对于\(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\)中元素不重复


先吐槽一波部分分吧...

明显有一个矩阵快速幂的做法,然后通过不了60pts,请问这个点的分怎么拿呢?

\(dp_{i,j}\)\(i\)个数乘积为\(j\)的方案

\[dp_{i,j}=\sum dp_{i-1,ab\% m=j} \]


思路:利用原根转换问题,然后生成函数用多项式快速幂求一下就行了。

对原根,若\(g\)\(m\)的原根,则\(g^0,g^1,\dots,g^{\varphi(m)-1}\)遍历\(\bmod m\)的最小剩余系,就是它们有个一一对应关系

关于求原根,把\(\varphi(m)-1\)分解质因数得到每个质因子\(p_i\),然后从小到大枚举原根\(g\),检验每个质因子是否有\(g^{\frac{\varphi(m)-1}{p_i}}\equiv 1 \pmod m\),如果所有质因子都没有,\(g\)就是原根,因为原根非常的小,所以这个复杂度是正确的。

然后把前面\(ab\%m=j\)换掉,就成了\((a’+b')\%(m-1)=j'\)

然后你构造生成函数\(f_i=\sum is_ix^{i-1}\)\(is_i\)代表最开始转换后这个值存不存在。

然后发现结果就是这个\(\tt f^n\),写一个\(NTT\)快速幂就成了,复杂度\(O(m\log m\log n)\)


Code:

#include <cstdio>
#include <algorithm>
const int N=(1<<14)+10;
const int mod=1004535809,Gi=334845270;
#define add(x,y,p) ((x+y)%p)
#define mul(x,y,p) (1ll*(x)*(y)%p)
int qp(int d,int k,int p){int f=1;while(k){if(k&1) f=mul(f,d,p);d=mul(d,d,p),k>>=1;}return f;}
int A[N],B[N],F[N],D[N],n,m,turn[N],to[N],len=1;
int GetG(int x)
{
    int s[1010]={0},phi=x-1,t=x-1;
    for(int i=2;i*i<=t;i++)
    {
        if(t%i==0)
        {
            s[++s[0]]=i;
            while(t%i==0) t/=i;
        }
    }
    if(t!=1) s[++s[0]]=t;
    for(int i=2;i<x;i++)
    {
        int flag=1;
        for(int j=1;j<=s[0];j++)
            if(qp(i,phi/s[j],x)==1)
            {
                flag=0;
                break;
            }
        if(flag) return i;
    }
    return -1;
}
void NTT(int *a,int typ)
{
    for(int i=0;i<len;i++) if(i<turn[i]) std::swap(a[i],a[turn[i]]);
    for(int le=1;le<len;le<<=1)
    {
        int wn=qp(typ?3:Gi,(mod-1)/(le<<1),mod);
        for(int p=0;p<len;p+=le<<1)
        {
            int w=1;
            for(int i=p;i<p+le;i++,w=mul(w,wn,mod))
            {
                int tx=a[i],ty=mul(w,a[i+le],mod);
                a[i]=add(tx,ty,mod);
                a[i+le]=add(tx,mod-ty,mod);
            }
        }
    }
    if(!typ)
    {
        int inv=qp(len,mod-2,mod);
        for(int i=0;i<len;i++) a[i]=mul(a[i],inv,mod);
    }
}
void Rev()
{
    int L=-1;for(int i=1;i<len;i<<=1) ++L;
    for(int i=1;i<len;i++) turn[i]=turn[i>>1]>>1|(i&1)<<L;
}
void polymul(int *a,int *b)
{
    for(int i=0;i<len;i++) A[i]=a[i],B[i]=b[i],a[i]=0;
    NTT(A,1),NTT(B,1);
    for(int i=0;i<len;i++) A[i]=mul(A[i],B[i],mod);
    NTT(A,0);
    for(int i=0;i<len;i++) a[i%(m-1)]=add(a[i%(m-1)],A[i],mod);
}
int main()
{
    int s,x;
    scanf("%d%d%d%d",&n,&m,&x,&s);
    int g=GetG(m);
    while(len<=(m<<1))len<<=1;Rev();
    for(int f=1,i=0;i<m-1;i++,f=mul(f,g,m))
        to[f]=i;
    for(int x,i=1;i<=s;i++)
    {
        scanf("%d",&x);
        if(x) F[to[x]]=1,D[to[x]]=1;
    }
    --n;
    while(n)
    {
        if(n&1) polymul(F,D);
        polymul(D,D);
        n>>=1;
    }
    printf("%d\n",F[to[x]]);
    return 0;
}

2018.12.17

posted @ 2018-12-17 19:46  露迭月  阅读(256)  评论(0编辑  收藏  举报