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

SDOI2015 序列统计

时间:2015-04-20 22:17:10      阅读:123      评论:0      收藏:0      [点我收藏+]

标签:

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
【样例说明】
可以生成的满足要求的不同的数列有(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)。
 

Data Constraint

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

解法:

首先可以想到一个暴力dp,设f[i][j]表示选到第i个数,mod m=j的方案数。那么显然转移方程为f[i+1][j*k%m]+=f[i][j];

即f[i+1][j*k%m]=∑f[i][j]*num[k](序列S中mod m=k的数的个数)

因为m是质数,所以m存在原根,我们可以通过离散对数的变换将乘法变为加法

原式变为f[i+1][(ind[j]+ind[k])%(m-1)]=∑f[i][ind[j]]*num[ind[k]]

为卷积形式,可使用FFT优化。

N很大,所以套一个快速幂即可

 

 

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>

using namespace std;
typedef long long ll;

int n,m,x,L,i,k,N,ws,NN;
int a[100011],pos[100011],b[100011],B[100011],d[100011];
int w[100011],ind[100011],h[100011];
int mo=1004535809;

int mi(int x,int z)
{
    int l;
    l=1;
    while(z){
        if(z%2==1)l=(ll)l*x%mo;
        z/=2;
        x=(ll)x*x%mo;
    }
    return l;
}

bool isroot(int x)
{
    int i,l;
    l=1;
    for(i=1;i<m-1;i++){
        l=l*x%m;
        if(l==1)return false;
    }
    return true;
}

void prepare()
{
    int i,j,k,g,l;
    //离散对数
    for(i=1;i<m;i++)if(isroot(i)){
        g=i;
        break;
    }
    l=1;
    for(i=1;i<m;i++){
        l=l*g%m;
        ind[l]=i;
    }
    //fftN次单位复数根
    N=1;
    while(N<2*m){
        N*=2;
        ws++;
    }
    w[0]=1;
    w[1]=mi(3,(mo-1)/N);
    for(i=2;i<=N;i++)w[i]=(ll)w[i-1]*w[1]%mo;
    for(i=0;i<N;i++){
        for(j=0;j<ws;j++){
            k=(i&(1<<(ws-1-j)));
            if(k)pos[i]+=(1<<j);
        }
    }
    NN=mi(N,mo-2);
}

void Dft(int *a,int sig)
{
    int i,half,u,v,j,l,wi;
    for(i=0;i<N;i++)h[pos[i]]=a[i];
    for(l=1;l<=ws;l++){
        half=1<<(l-1);
        for(i=0;i<half;i++){
            wi=(sig>0)?w[i<<(ws-l)]:w[N-(i<<(ws-l))];
            for(j=i;j<N;j+=(1<<l)){
                u=h[j];v=(ll)h[j+half]*wi%mo;
                h[j]=(u+v)%mo;
                h[j+half]=((u-v)%mo+mo)%mo;
            }
        }
    }
    for(i=0;i<N;i++)a[i]=h[i];
}

void fft(int *a,int *b)
{
    int i;
    Dft(a,1);
    Dft(b,1);
    for(i=0;i<N;i++)a[i]=(ll)a[i]*b[i]%mo;
    Dft(a,-1);    
    for(i=0;i<N;i++)a[i]=(ll)a[i]*NN%mo;
    for(i=m;i<N;i++){
        a[(i%(m-1)==0)?(m-1):(i%(m-1))]=(a[(i%(m-1)==0)?(m-1):(i%(m-1))]+a[i])%mo;
        a[i]=0;
    }
}

void MT(int *b,int z)
{
    int i;
    d[0]=1;
    while(z){
        if(z%2==1){
            for(i=0;i<N;i++)B[i]=b[i];
            fft(d,b);
            for(i=0;i<N;i++)b[i]=B[i];
        }
        z/=2;
        for(i=0;i<N;i++)B[i]=b[i];
        fft(b,B);
    }
}

void Work()
{
    a[ind[1]]++;
    MT(b,n);
    fft(a,d);
    printf("%d\n",a[ind[x]]); 
}

int main()
{
    scanf("%d%d%d%d",&n,&m,&x,&L);    
    prepare();
    for(i=1;i<=L;i++){
        scanf("%d",&k);
        k%=m;
        b[ind[k]]++;
    }
    b[0]=0;
    Work();
}

 

SDOI2015 序列统计

标签:

原文地址:http://www.cnblogs.com/applejxt/p/4442601.html

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