码迷,mamicode.com
首页 > 其他好文 > 详细

[bzoj 3992][SDOI 2015]序列统计

时间:2019-01-10 15:40:36      阅读:161      评论:0      收藏:0      [点我收藏+]

标签:str   printf   原根   gis   pow   pos   sdoi   +=   int   

传送门

Description

小C有一个集合\(S\),里面的元素都是小于\(M\)的非负整数。他用程序编写了一个数列生成器,可以生成一个长度为\(N\)的数列,数列中的每个数都属于集合\(S\)。小C用这个生成器生成了许多这样的数列。但是小C有一个问题需要你的帮助:

  • 给定整数\(x\),求所有可以生成出的,且满足数列中所有数的乘积\(mod \ M\) 的值等于\(x\)的不同的数列的有多少个。小C认为,两个数列\({A_i}\)\({B_i}\)不同,当且仅当至少存在一个整数\(i\),满足\(A_i≠B_i\)。另外,小C认为这个问题的答案可能很大,因此他只需要你帮助他求出答案\(mod \ \ 1004535809\)的值就可以了。

Solution

仍然是用生成函数来做,但是,并不能实现下标乘的卷积

发现条件中给出\(M\)一定是个质数,看到质数就想到原根,把每个数都用原根的幂来表示,化乘为加

\(M \leq 8000\),它的原根直接暴力求就可以了

剩下的就是\(NTT\)优化多项式快速幂了


Code?

#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
#define mod 1004535809
#define g 3
#define invg 334845270
#define MN 8005
inline int fpow(int x,int m,int p=mod)
{
    register int ret=1;
    for(;m;x=1ll*x*x%p,m>>=1)if(m&1)ret=1ll*ret*x%p;
    return ret;
}
int ct,fac[MN];
inline int getg(int p)
{
    register int i,j;ct=0;
    for(i=2;i<=p-2;++i) if((p-1)%i==0) fac[++ct]=i;
    for(i=2;;++i)
    {
        register bool yes=true;
        for(j=1;j<=ct;++j) if(fpow(i,fac[j],p)==1) {yes=false;break;}
        if(yes) return i; 
    }
}
int G,mi[MN],s[MN<<2],t[MN<<2],N,di,pos[MN<<2],invN,M;
inline void NTT(int *a,int type)
{
    register int i,j,p,k;
    for(i=0;i<N;++i)if(i<pos[i]) std::swap(a[i],a[pos[i]]);
    for(i=1;i<N;i<<=1)
    {
        ll wn=fpow(type>0?g:invg,(mod-1)/(i<<1));
        for(p=i<<1,j=0;j<N;j+=p) 
        {
            ll w=1;
            for(k=0;k<i;++k,w=w*wn%mod)
            {
                ll X=a[j+k],Y=w*a[j+i+k]%mod;
                a[j+k]=(X+Y)%mod;a[j+i+k]=(X-Y+mod)%mod;
            }
        }
    }
}
inline void self(int *a)
{
    register int i;
    NTT(a,1);for(i=0;i<N;++i) a[i]=1ll*a[i]*a[i]%mod;
    NTT(a,-1);for(i=0;i<N;++i) a[i]=1ll*a[i]*invN%mod;
    for(i=N-1;i>=M-1;--i) (a[i-M+1]+=a[i])%=mod,a[i]=0;
}
inline void pro(int *a,int *b)
{
    register int i;
    NTT(a,1);NTT(b,1);for(i=0;i<N;++i) a[i]=1ll*a[i]*b[i]%mod;
    NTT(a,-1);NTT(b,-1);
    for(i=0;i<N;++i) a[i]=1ll*a[i]*invN%mod;
    for(i=0;i<N;++i) b[i]=1ll*b[i]*invN%mod;
    for(i=N-1;i>=M-1;--i) (a[i-M+1]+=a[i])%=mod,a[i]=0;
}
inline void pow(int *a,int m)
{
    register int i;
    for(N=1;N<=M+M-2;N<<=1,di++);
    invN=fpow(N,mod-2);
    for(i=0;i<N;++i) pos[i]=(pos[i>>1]>>1)|((i&1)<<(di-1));
    for(t[0]=1;m;self(a),m>>=1) if(m&1) pro(t,a);
}
int main()
{
    register int i,n,x,S,tmp;
    n=read();M=read();x=read();S=read();G=getg(M);
    for(i=tmp=1;i<M;++i) tmp=1ll*tmp*G%M,mi[tmp]=i%(M-1);
    for(i=1;i<=S;++i) tmp=read(),tmp?s[mi[tmp]]=1:0;
    pow(s,n);return 0*printf("%d\n",t[mi[x]]);
}



Blog来自PaperCloud,未经允许,请勿转载,TKS!

[bzoj 3992][SDOI 2015]序列统计

标签:str   printf   原根   gis   pow   pos   sdoi   +=   int   

原文地址:https://www.cnblogs.com/PaperCloud/p/10249777.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!