• [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]]\% varphileft(M ight)$
    套用上面的算法即可
    还有$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
    ", b[ind[x % m]]);
        return 0;
    }
  • 相关阅读:
    第二次作业
    java第一次上机练习
    java第一次作业
    第四周上机作业
    第三周作业
    第一次上机作业
    第一次作业
    3.19第三周上机作业
    第一周课下作业
    2020.3.23 java第三次作业
  • 原文地址:https://www.cnblogs.com/ruoruoruo/p/7784728.html
Copyright © 2020-2023  润新知