[BZOJ3992][SDOI2015]序列统计

Posted xjr_01

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOJ3992][SDOI2015]序列统计相关的知识,希望对你有一定的参考价值。

[BZOJ3992][SDOI2015]序列统计

试题描述

小C有一个集合 \(S\),里面的元素都是小于 \(M\) 的非负整数。他用程序编写了一个数列生成器,可以生成一个长度为 \(N\) 的数列,数列中的每个数都属于集合 \(S\)。小C用这个生成器生成了许多这样的数列。但是小C有一个问题需要你的帮助:

给定整数 \(x\),求所有可以生成出的,且满足数列中所有数的乘积 \(\mod M\) 的值等于 \(x\) 的不同的数列的有多少个。小C认为,两个数列 \(\{ A_i \}\)\(\{ B_i \}\)不同,当且仅当至少存在一个整数 \(i\),满足 \(A_i \ne B_i\)。另外,小C认为这个问题的答案可能很大,因此他只需要你帮助他求出答案 \(\mod 1004535809\) 的值就可以了。

输入

第一行,四个整数,\(N\)\(M\)\(x\)\(|S|\),其中 \(|S|\) 为集合 \(S\) 中元素个数。

第二行,\(|S|\) 个整数,表示集合 \(S\) 中的所有元素。

输出

一行,一个整数,表示你求出的种类数 \(\mod 1004535809\) 的值。

输入示例

4 3 1 2
1 2

输出示例

8

样例解释

技术分享图片

数据规模及约定

\(1 \le N \le 10^9\)\(3 \le M \le 8000\)\(M\) 为质数

\(0 \le x \le M-1\),输入数据保证集合 \(S\) 中元素不重复

题解

一提到原根应该就会做了。会做的就不用再往下看了……

原根的定义中运算都在模 \(P\) 意义下,原根就是一个整数 \(g\),满足 \(\{ g^i | i \in [0, P-2] \}\) 能与 \(\{ i | i \in [1, P-1] \}\) 一一对应。找原根的方法是暴力枚举 \(g\),检查是否 \(g^k \equiv 1\) 是否是 \(k = P-1 或 k = 0\) 的充要条件,是则表明 \(g\) 是原根,否则继续枚举。

这样,每个非 \(0\) 整数都能够用 \(g\) 的若干次方来表示了,并且乘积变成了指数的相加。于是就可以解决“转移是乘法”的问题了。

按照套路,还是讲一下暴力 dp 吧。设 \(f(i, j)\) 表示长度为 \(i\) 的数列,数列乘积的结果是 \(g^j \mod M\),这样的数列的个数。那么我们找到 \(S\) 中的元素的指数,即找到 \(P_i\) 满足 \(g^{P_i} \equiv S_i(\mod M)\),那么转移就是

\[ f(i, j) \rightarrow f(i, (j + P_t) \mod (M-1)), t \in [1, |S|] \f(0, 0) = 1 \]

其中,\(\rightarrow\) 表示累加。

然后搞生成函数 \(F_i(x)\)\(f(i, j)\) 的生成函数,令 \(G(x) = \sum_{i=1}^{|S|} x^{P_i}\),有

\[ F_i(x) = F_{i-1}(x) \cdot G(x) \F_0(x) = 1 \]

注意这里的多项式乘法是 \(M-1\) 位循环卷积。上面的式子可以用倍增 + NTT 来做,每次乘法完毕之后暴力把多出来的部分累加到前面去。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
#define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)

int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); }
    return x * f;
}

#define maxn 16384
#define MOD 1004535809
#define Groot 3
#define LL long long

int Pow(int a, int b) {
    int ans = 1, t = a;
    while(b) {
        if(b & 1) ans = (LL)ans * t % MOD;
        t = (LL)t * t % MOD; b >>= 1;
    }
    return ans;
}

int brev[maxn];
void FFT(int *a, int len, int tp) {
    int n = 1 << len;
    rep(i, 0, n - 1) if(i < brev[i]) swap(a[i], a[brev[i]]);
    rep(i, 1, len) {
        int wn = Pow(Groot, MOD - 1 >> i);
        if(tp < 0) wn = Pow(wn, MOD - 2);
        for(int j = 0; j < n; j += 1 << i) {
            int w = 1;
            rep(k, 0, (1 << i >> 1) - 1) {
                int la = a[j+k], ra = (LL)w * a[j+k+(1<<i>>1)] % MOD;
                a[j+k] = (la + ra) % MOD;
                a[j+k+(1<<i>>1)] = (la - ra + MOD) % MOD;
                w = (LL)w * wn % MOD;
            }
        }
    }
    if(tp < 0) {
        int invn = Pow(n, MOD - 2);
        rep(i, 0, n - 1) a[i] = (LL)a[i] * invn % MOD;
    }
    return ;
}
void Mul(int *A, int *B, int n, int m, bool recover = 0) {
    int N = 1, len = 0;
    while(N <= n + m) N <<= 1, len++;
    rep(i, 0, N - 1) brev[i] = (brev[i>>1] >> 1) | ((i & 1) << len >> 1);
    rep(i, n + 1, N - 1) A[i] = 0;
    rep(i, m + 1, N - 1) B[i] = 0;
    FFT(A, len, 1); FFT(B, len, 1);
    rep(i, 0, N - 1) A[i] = (LL)A[i] * B[i] % MOD;
    FFT(A, len, -1); if(recover) FFT(B, len, -1);
    rep(i, n + 1, n + m) (A[i%(n+1)] += A[i]) %= MOD;
    return ;
}

int findg(int M) {
    rep(i, 2, M - 1) {
        int x = 1; bool ok = 1;
        rep(j, 1, M - 2) {
            x = (LL)x * i % M;
            if(x == 1){ ok = 0; break; }
        }
        if(ok) return i;
    }
    return 0;
}
int s[maxn], p[maxn], Exp[maxn];
void getExp(int g, int M, int k) {
    int x = 1;
    rep(i, 0, M - 2) Exp[x] = i, x = (LL)x * g % M;
    rep(i, 1, k) p[i] = s[i] ? Exp[s[i]] : -1;
    return ;
}

int F[maxn], G[maxn], tmp[maxn];
void p_pow(int n, int M) {
    while(n) {
        if(n & 1) Mul(F, G, M, M, 1);
        memcpy(tmp, G, sizeof(G));
        Mul(G, tmp, M, M); n >>= 1;
    }
    return ;
}

int main() {
    int n = read(), M = read(), q = read(), k = read(), g = findg(M);
    rep(i, 1, k) s[i] = read();
    
    getExp(g, M, k);
    rep(i, 1, k) if(p[i] >= 0) G[p[i]] = 1;
    F[0] = 1;
    p_pow(n, M - 2);
    
    printf("%d\n", F[Exp[q]]);
    
    return 0;
}

以上是关于[BZOJ3992][SDOI2015]序列统计的主要内容,如果未能解决你的问题,请参考以下文章

[BZOJ3992][SDOI2015]序列统计

[BZOJ3992][SDOI2015]序列统计

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

BZOJ 3992 [SDOI2015]序列统计

Bzoj3992:[SDOI2015]序列统计

[BZOJ3992][SDOI2015]序列统计(DP+原根+NTT)