[BZOJ3992][SDOI2015]序列统计

3992: [SDOI2015]序列统计

Time Limit: 30 Sec  Memory Limit: 128 MB Submit: 1522  Solved: 709 [Submit][Status][Discuss]

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中元素不重复
 
题目要求$X=\prod s[i]\%M$的方案数
可以发现如果那个相乘是相加的话就很很好做
考虑求$X=\sum s[i]\%P$的方案数
那么可以构造母函数$f(x)$,取$n$个数即为求$f(x)^{n}$的第$X$项的系数
至于模$P$的话只需在乘的过程中将系数超过$P$的加到前面来即可
使用快速幂和NTT加速多项式乘法
注意到M是个小质数,那么乘法可以转化为加法加法的情况
先求出$M$的原根,再求出$0$到$M-1$每个数的指标$ind[i]$
进而$X=\prod s[i]\% M$就化为了$ind[X]=\sum ind[s[i]]\% \varphi\left(M\right)$
套用上面的算法即可
还有$1004535809$的原根是$3$
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int Max = 1 << 14, P = 479 << 21 | 1;
const ll g = 3;
ll wn[20];
inline ll ksm(ll a, ll b, const ll &mod){
    ll s = 1;
    while(b){
        if(b & 1) s = s * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return s;
}
void change(ll s[], int len){
    for(int i = 1, j = len >> 1, k; i < len - 1; i++){
        if(i < j) swap(s[i], s[j]);
        k = len >> 1;
        while(j >= k){
            j -= k;
            k >>= 1;
        }
        if(j < k) j += k;
    }
}
void NTT(ll s[], int len, int on){
    change(s, len);
    for(int id = 0, h = 2; h <= len; h <<= 1){
        id++;
        for(int j = 0; j < len; j += h){
            ll w = 1;
            for(int k = j; k < j + (h >> 1); k++){
                ll u = s[k] % P, t = w * s[k + (h >> 1)] % P;
                s[k] = (u + t) % P;
                s[k + (h >> 1)] = (u - t + P) % P;
                w = w * wn[id] % P;
            }
        }
    }
    if(on == -1){
        for(int i = 1; i < (len >> 1); i++) swap(s[i], s[len - i]);
        ll inv = ksm(len, P - 2, P);
        for(int i = 0; i < len; i++) s[i] = s[i] * inv % P;
    }
}
ll Getg(const ll &x){
    ll t = x - 1, p[20];
    p[0] = 0;
    for(ll i = 2; i * i <= t; i++){
        if(t % i == 0){
            p[++p[0]] = i;
            while(t % i == 0) t /= i;
        }
    }
    if(t != 1) p[++p[0]] = t;
    bool flag;
    for(ll i = 1; i <= x; i++){
        flag = true;
        for(int j = 1; j <= p[0]; j++)
            if(ksm(i, (x - 1) / p[j], x) == 1){
                flag = false;
                break;
            }
        if(flag) return i;
    }
}
int n, m, x, s;
ll a[Max] = {0}, b[Max] = {0};
int ref[8000 + 10];
int main(){
    for(int i = 0; i < 20; i++) wn[i] = ksm(g, (P ^ 1) >> i, P);
    scanf("%d %d %d %d", &n, &m, &x, &s);
    ll gg = Getg(m);
    for(int t = 1, i = 0; i < m - 1; i++){
        ref[t] = i;
        t = t * gg % m;
    }
    for(int t, i = 1; i <= s; i++){
        scanf("%d", &t);
        if(t) a[ref[t]] = 1;
    }
    m--;// m = phi(m);
    int len = 1;
    while(len <= (m << 1)) len <<= 1;
    for(int i = 0; i <= m; i++) b[i] = a[i];
    n--;
    while(n){
        NTT(a, len, 1);
        if(n & 1){
            NTT(b, len, 1);
            for(int i = 0; i < len; i++) b[i] = b[i] * a[i] % P;
            NTT(b, len, -1);
            for(int i = m; i < len; i++){
                b[i % m] = (b[i % m] + b[i]) % P;
                b[i] = 0;
            }
        }
        n >>= 1;
        if(!n) break;
        for(int i = 0; i < len; i++) a[i] = a[i] * a[i] % P;
        NTT(a, len, -1);
        for(int i = m; i < len; i++){
            a[i % m] = (a[i % m] + a[i]) % P;
            a[i] = 0;
        }
    }
    printf("%lld\n", b[ind[x % m]]);
    return 0;
}

 

posted @ 2017-11-04 21:49  jzyy  阅读(115)  评论(0编辑  收藏  举报