洛谷传送门
BZOJ传送门
题目描述
小C有一个集合 S S S,里面的元素都是小于 M M M的非负整数。他用程序编写了一个数列生成器,可以生成一个长度为 N N N的数列,数列中的每个数都属于集合 S S S。小C用这个生成器生成了许多这样的数列。但是小C有一个问题需要你的帮助:给定整数 x x x,求所有可以生成出的,且满足数列中所有数的乘积 m o d M mod\ M mod M的值等于 x x x的不同的数列的有多少个。小C认为,两个数列 { A i } \{A_i\} {Ai}和 { B i } \{B_i\} {Bi}不同,当且仅当至少存在一个整数 i i i,满足 A i ≠ B i A_i≠B_i Ai̸=Bi。另外,小C认为这个问题的答案可能很大,因此他只需要你帮助他求出答案 m o d 1004535809 mod\ 1004535809 mod 1004535809的值就可以了。
输入输出格式
输入格式:
一行,四个整数, N N N、 M M M、 x x x、 ∣ S ∣ |S| ∣S∣,其中 ∣ S ∣ |S| ∣S∣为集合 S S S中元素个数。第二行, ∣ S ∣ |S| ∣S∣个整数,表示集合 S S S中的所有元素。
输出格式:
一行,一个整数,表示你求出的种类数 m o d 1004535809 mod\ 1004535809 mod 1004535809的值。
输入输出样例
输入样例#1:
4 3 1 2
1 2
输出样例#1:
8
说明
【样例说明】
可以生成的满足要求的不同的数列有(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 1\le N\le 1000 1≤N≤1000;
对于30%的数据, 3 ≤ M ≤ 100 3\le M\le 100 3≤M≤100;
对于60%的数据, 3 ≤ M ≤ 800 3\le M\le 800 3≤M≤800;
对于全部的数据, 1 ≤ N ≤ 1 0 9 1\le N\le 10^9 1≤N≤109, 3 ≤ M ≤ 8000 3\le M\le 8000 3≤M≤8000, M M M为质数, 1 ≤ x ≤ M − 1 1\le x\le M-1 1≤x≤M−1,输入数据保证集合 S S S中元素不重复
解题分析
看到 M M M是个小质数, 而我们要求的是一个余数, 显然可以转化为原根的次幂, 这样就将乘法转化为了加法。
发现方案的转移是一个卷积, 这样直接大力 N T T NTT NTT加上快速幂转移就好。
总复杂度 O ( M l o g ( M ) l o g ( N ) ) O(Mlog(M)log(N)) O(Mlog(M)log(N))。
代码如下:
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include <cstring>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 40050
#define G 3
#define Ginv 334845270
#define MOD 1004535809
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc);
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
}
IN int fpow(R int base, R int tim, R int mod)
{
int ret = 1;
W (tim)
{
if (tim & 1) ret = 1ll * ret * base % mod;
base = 1ll * base * base % mod, tim >>= 1;
}
return ret;
}
int N, M, x, S, fcnt, bd;
int rev[MX], res[MX], a[MX], b[MX], cnt[MX], fac[MX], pos[MX];
IN void NTT(int *dat, R int len, R bool typ)
{
for (R int i = 0; i < len; ++i) if (rev[i] > i) std::swap(dat[i], dat[rev[i]]);
R int seg, now, cur, step, bd, buf1, buf2, deal, base;
for (seg = 1; seg < len; seg <<= 1)
{
base = fpow(typ ? G : Ginv, (MOD - 1) / (seg << 1), MOD); step = seg << 1;
for (now = 0; now < len; now += step)
{
deal = 1, bd = now + seg;
for (cur = now; cur < bd; ++cur, deal = 1ll * deal * base % MOD)
{
buf1 = dat[cur], buf2 = 1ll * dat[cur + seg] * deal % MOD;
dat[cur] = (buf1 + buf2) % MOD, dat[cur + seg] = (buf1 - buf2 + MOD) % MOD;
}
}
}
if (typ) return; int inv = fpow(len, MOD - 2, MOD);
for (R int i = 0; i < len; ++i) dat[i] = 1ll * dat[i] * inv % MOD;
}
void pre()
{
int bd = std::sqrt(M - 1), tmp = M - 1;
for (R int i = 2; i <= bd; ++i)
{
if (!(tmp % i))
{
fac[++fcnt] = i;
if (i != tmp / i) fac[++fcnt] = tmp / i;
}
}
}
IN bool check(R int val)
{
for (R int i = 1; i <= fcnt; ++i)
if (fpow(val, fac[i], M) == 1) return false;
return true;
}
IN void find()
{
for (R int i = 2; i < M; ++i)
if (check(i))
{
int bd = M - 1, now = 1;
for (R int j = 0; j < bd; ++j, now = now * i % M) pos[now] = j;
return;
}
}
IN void Mul(int *res, int *mul, R int len)
{
for (R int i = bd; i < len; ++i) a[i] = b[i] = 0;
for (R int i = 0; i < bd; ++i) a[i] = res[i], b[i] = mul[i], res[i] = 0;
NTT(a, len, 1), NTT(b, len, 1);
for (R int i = 0; i < len; ++i) a[i] = 1ll * a[i] * b[i] % MOD;
NTT(a, len, 0);
for (R int i = 0; i < len; ++i) (res[i % bd] += a[i]) %= MOD;
}
int main(void)
{
int foo;
in(N), in(M), in(x), in(S);
pre(); find(); bd = M - 1;
for (R int i = 1; i <= S; ++i)
{
in(foo); foo %= M;
if (!foo) continue;
++cnt[pos[foo]];
}
int lg = 0, len = 1; --N;
for (R int i = 0; i < bd; ++i) res[i] = cnt[i];
for (; len <= (bd << 1); lg++, len <<= 1);
for (R int i = 1; i < len; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << lg - 1);
W (N)
{
if (N & 1) Mul(res, cnt, len);
Mul(cnt, cnt, len), N >>= 1;
}
printf("%d", res[pos[x]]);
}