【NTT】BZOJ3992序列统计

分析:

这道题与其他题目的最大区别,在于这里变成了累乘,而不是累加。

这样就不能直接用NTT搞了。

所以要转化一下问题:引入原根。

原根的一大性质就是,若对于一个质数p,其一个原根为g
g0,g1,g2,gp2(mod p) g 0 , g 1 , g 2 , … … g p − 2 ( m o d   p ) 构成了p的一个既约剩余系(即mod p 的意义下各不相同)。

这样一来,对于每一个值,我们将其映射到g的某个次方,这样一来,每次相乘就是指数相加。

注:0要被忽略。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 20000
using namespace std;
typedef long long ll;
int G=3;
const ll MOD=1004535809;
const int siz=16384;
ll fsp(ll x,int y,ll mod){
    ll res=1;
    while(y){
        if(y&1) res=(res*x)%mod;
        x=(x*x)%mod;
        y>>=1;
    }
    return res; 
}
void ntt(ll *a,int f,int N){
    int i,j,k;
    for(i=1,j=0;i<N;i++){
        for(int d=N;j^=d>>=1,~j&d;);
        if(i<j)
            swap(a[i],a[j]);
    }
    for(i=1;i<N;i<<=1){
        ll wn=fsp(G,(MOD-1)/(2ll*i),MOD);
        if(f==0)
            wn=fsp(wn,MOD-2,MOD);
        for(j=0;j<N;j+=i<<1){
            ll w=1;
            for(k=0;k<i;k++,w=(w*wn)%MOD){
                ll x=a[j+k],y=(w*a[i+j+k])%MOD;
                a[j+k]=(x+y)%MOD;
                a[i+j+k]=(x-y+MOD)%MOD;
            }
        }
    }
    if(f==0){
        ll inv=fsp(N,MOD-2,MOD);
        for(int i=0;i<N;i++)
            a[i]=(a[i]*inv)%MOD;
    }
}
ll A[MAXN],B[MAXN];
int n,x,m,s;
vector<int> pr;
int find_g(int x){
    pr.clear();
    int x1=x-1;
    for(int i=2;i<=x1;i++)
        if(x1%i==0){
            pr.push_back(i);
            while(x1%i==0)
                x1/=i;  
        }
    for(int i=2;i<x;i++){
        bool flag=0;
        for(int j=0;j<pr.size();j++){
            if(fsp(i,(x-1)/pr[j],x)==1)
                flag=1;
        }
        if(flag==0)
            return i;
    }
}
int tas[MAXN],xx;
int main(){
    SF("%d%d%d%d",&n,&m,&xx,&s);
    int g=find_g(m);
    int sum=1;
    for(int i=0;i<m-1;i++){
        tas[sum]=i;
        sum=sum*g%m;
    }
    for(int i=0;i<s;i++){
        SF("%d",&x);
        if(x==0)
            continue;
        B[tas[x]]=1;
    }
    A[0]=1;
    m--;
    ntt(B,1,siz);
    while(n){
        if(n&1){
            ntt(A,1,siz);
            for(int i=0;i<siz;i++)
                A[i]=A[i  ]*B[i]%MOD;
            ntt(A,0,siz);
            for(int i=m;i<siz;i++){
                A[i%m]=(A[i%m]+A[i])%MOD;
                A[i]=0;
            }
        }
        for(int i=0;i<siz;i++)
            B[i]=B[i]*B[i]%MOD;
        ntt(B,0,siz);
        for(int i=m;i<siz;i++){
            B[i%m]=(B[i%m]+B[i])%MOD;
            B[i]=0;
        }
        ntt(B,1,siz);
        n>>=1;
    }
    PF("%lld",A[tas[xx]]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值