BZOJ 3992: [SDOI2015]序列统计 快速幂+NTT(离散对数下)

3992: [SDOI2015]序列统计

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

HINT

【样例说明】

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

【数据规模和约定】

对于10%的数据,1<=N<=1000;

对于30%的数据,3<=M<=100;

对于60%的数据,3<=M<=800;

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

Source

题解:

  利用原根G

  将序列的乘积替换成加法

  想快速幂那般的计算,得到长度为n的序列

  每次NTT加速

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define ls i<<1
#define rs ls | 1
#define mid ((ll+rr)>>1)
#define pii pair<int,int>
#define MP make_pair
typedef long long LL;
typedef unsigned long long ULL;
const long long INF = 1e18+1LL;
const double pi = acos(-1.0);
const int N = 6e4+10, M = 2e2+20,inf = 2e9;
int MOD;
inline int muls(int a, int b){
    return (long long)a * b % MOD;
}
int power(int a, int b){
    int ret = 1;
    for (int t = a; b; b >>= 1){
        if (b & 1)ret = muls(ret, t);
        t = muls(t, t);
    }
    return ret;
}
int cal_root(int mod)
{
    int factor[20], num = 0, s = mod - 1;
    MOD = mod--;
    for (int i = 2; i * i <= s; i++){
        if (s % i == 0){
            factor[num++] = i;
            while (s % i == 0)s /= i;
        }
    }
    if (s != 1)factor[num++] = s;
    for (int i = 2;; i++){
        int j = 0;
        for (; j < num && power(i, mod / factor[j]) != 1; j++);
        if (j == num)return i;
    }
}

const LL G = 3LL, P = 1004535809LL;
LL mul(LL x,LL y){
    return (x*y-(LL)(x/(long double)P*y+1e-3)*P+P)%P;
}
LL qpow(LL x,LL k,LL p){
    LL ret=1;
    while(k){
        if(k&1) ret=mul(ret,x);
        k>>=1;
        x=mul(x,x);
    }
    return ret;
}
LL wn[50];
void getwn(){
    for(int i=1; i<=35; ++i){
        int t=1<<i;
        wn[i]=qpow(G,(P-1)/t,P);
    }
}int len;
void NTT_init() {
    getwn();
}

void NTT(LL y[],int op){
    for(int i=1,j=len>>1,k; i<len-1; ++i){
        if(i<j) swap(y[i],y[j]);
        k=len>>1;
        while(j>=k){
            j-=k;
            k>>=1;
        }
        if(j<k) j+=k;
    }
    int id=0;
    for(int h=2; h<=len; h<<=1) {
        ++id;
        for(int i=0; i<len; i+=h){
            LL w=1;
            for(int j=i; j<i+(h>>1); ++j){
                LL u=y[j],t=mul(y[j+h/2],w);
                y[j]=u+t;
                if(y[j]>=P) y[j]-=P;
                y[j+h/2]=u-t+P;
                if(y[j+h/2]>=P) y[j+h/2]-=P;
                w=mul(w,wn[id]);
            }
        }
    }
    if(op==-1){
        for(int i=1; i<len/2; ++i) swap(y[i],y[len-i]);
        LL inv=qpow(len,P-2,P);
        for(int i=0; i<len; ++i) y[i]=mul(y[i],inv);
    }
}
LL mo[N],fmo[N],now[N],xx[N],yy[N];
int m;
void cal(LL *x,LL *y,LL *z) {
    for(int i = 0; i < len; ++i) xx[i] = x[i],yy[i] = y[i];
    NTT(xx,1);NTT(yy,1);
    for(int i = 0; i < len; ++i) z[i] = xx[i] * yy[i] % P,now[i] = 0;
    NTT(z,-1);

    for(int i = 0; i <= 2*m-2; ++i)
        now[mo[i%(m-1)]] += z[i],z[i] = 0;

    for(int i = 0; i < len; ++i) z[i] = 0;
    for(int i = 0; i <= m-1; ++i) {
        z[fmo[i]] += now[i];
    }
}
LL y[N],ans[N],ny[N],num[N],an[N];
int n,x,s,root,cnt0;

int main() {
    NTT_init();
    scanf("%d%d%d%d",&n,&m,&x,&s);
    root = cal_root(m);
    for(int i = 0, t = 1; i < m-1; ++i,t = t*root%m)
        mo[i] = t,fmo[t] = i;
    for(int i = 1; i <= s; ++i) {
        int xx;
        scanf("%d",&xx);
        if(xx == 0)
            cnt0+=1;
        else
            num[fmo[xx]]+=1;
    }
    if(x == 0) {
        LL L = (qpow(2LL,cnt0,P) - 1 + P) % P + qpow(2LL,s-cnt0,P)%P;
        printf("%lld\n",L%P);
        return 0;
    }
    len = 1;
    int f = 1;
    while(len <= 2*m) len<<=1;
    for(int i = 0; i < len; ++i) y[i] = num[i],ans[i] = 0;
    int j = 1;
    int chan = 0;
    while(n) {
        if(n&1) {
            if(f) {
                f = 0;
                for(int i = 0; i < len; ++i) ans[i] = y[i];
            }
            else {
                cal(ans,y,ans);
            }
        }
        cal(y,y,y);
        n>>=1;
    }
    int l = 0;
    for(int i = 0; i < len; ++i) {
        an[mo[i%(m-1)]] += ans[i],an[mo[i%(m-1)]]%=P;
    }
    cout<<an[x]<<endl;
    return 0;
}
时间: 2024-10-06 19:29:03

BZOJ 3992: [SDOI2015]序列统计 快速幂+NTT(离散对数下)的相关文章

BZOJ 3992: [SDOI2015]序列统计 [快速数论变换 生成函数 离散对数]

3992: [SDOI2015]序列统计 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 1017  Solved: 466[Submit][Status][Discuss] Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S. 小C用这个生成器生成了许多这样的数列.但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中

BZOJ 3992 Sdoi2015 序列统计 快速数论变换

题目大意:给定n(n<=10^9),质数m(3<=m<=8000),1<=x=m,以及一个[0,m-1]区间内的集合S,求有多少长度为n的数列满足每个元素都属于集合S且所有元素的乘积mod m后=x 求原根,对S集合内每个元素取指标,然后搞出生成函数f(x) 那么答案就是(f(x))^n (mod x^(m-1),mod 1004535809) 上NTT用多项式快速幂搞一搞就好了 #include <cstdio> #include <cstring> #i

BZOJ 3992: [SDOI2015]序列统计 NTT+快速幂

3992: [SDOI2015]序列统计 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 1155  Solved: 532[Submit][Status][Discuss] Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S. 小C用这个生成器生成了许多这样的数列.但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中

BZOJ[3992][SDOI2015]序列统计 生成函数+NTT

首先了解一下指标 看我瞎bb也可以 因为原根\(g\)满足\(g^i,g^j(i,j\in (1,MOD-1),i\neq j)\)互不相同 则可以给每个数\(i\)定义一个指标\(ind_i\)表示模意义下的\(\log_g i\),并且在区间\([1,\varphi(MOD)]\)中是互不相同的 和\(log\)类似,指标也满足\(ind_{i*j}\equiv ind_i+ind_j\)就可以把乘法弄成加法了 题目要求的\((a×b)mod M=x\)等价于\((ind[a]+ind[b]

bzoj 3992: [SDOI2015]序列统计

膝盖++,IQ-- SD总是酱紫.....吐槽+++++++ 这个乘积的形式是可以用他的原根表示成加法的!!神奇啊!!! 然后加法就很棒棒了,我们可以用生成函数这个东西来计算一下了. 然后NTT就好了!! 还有这里有一个像快速幂的东西,而且把大于模数的东西搞小,是循环卷积的形式吗??好神奇啊 原根真的是劲啊 1 #include <cstdio> 2 #include <iostream> 3 #include <cmath> 4 #define LL long lon

【BZOJ3992】[SDOI2015]序列统计 NTT+多项式快速幂

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

BZOJ 1008 越狱 题解 裸快速幂

BZOJ 1008 越狱 题解 裸快速幂 1008: [HNOI2008]越狱 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 7887  Solved: 3372[Submit][Status][Discuss] Description 监狱有连续编号为1...N的N个房间,每个房间关押一个犯人,有M种宗教,每个犯人可能信仰其中一种.如果相邻房间的犯人的宗教相同,就可能发生越狱,求有多少种状态可能发生越狱 Input 输入两个整数M,N.1<=M<

CF632E: Thief in a Shop(快速幂+NTT)(存疑)

A thief made his way to a shop. As usual he has his lucky knapsack with him. The knapsack can contain k objects. There are n kinds of products in the shop and an infinite number of products of each kind. The cost of one product of kind i is ai. The t

P3321 [SDOI2015]序列统计 FFT+快速幂+原根

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