给定元素在[0,m)内的整数集合S,求有多少个长度为n的数列满足所有元素属于S且mod m下的积为x。
由于m是质数,所以元素的积利用原根可转化为和。
即
由于原根的幂可以一一表示[0,m)内的数,所以
令 hi,hx满足ghimodm=ei,ghxmodm=x
于是问题转化为在S中可重复地选出n个元素使和为指定值。
考虑写个DP,然后发现和多项式乘法可以对应上。
而如果令第 hi 项的系数为1,那么该多项式的 n 次幂的第
多项式快速幂即可。
乘的时候注意元素和取模。
求原根之法:http://blog.youkuaiyun.com/acdreamers/article/details/8883285
然后1004535809的原根是3
#include <cstdio>
#include <algorithm>
#define rep(i,x,y) for(i=x;i<y;++i)
#define FOR(i,x,y) for(i=x;i<=y;++i)
using namespace std;
typedef long long ll;
const int MOD = 1004535809, N = 18000;
ll qpow(ll a, ll b, ll mod = MOD) {
ll c = 1;
for ( ; b; b >>= 1, a = a * a % mod)
if (b & 1) c = c * a % mod;
return c;
}
struct Alg {
int n, m, rev[N];
ll inv_n, w[2][N], t[N];
void init(int c) {
m = c; int k = -1;
for (n = 1; n < 2 * m - 1; n <<= 1) ++k; inv_n = qpow(n, MOD - 2);
int G = qpow(3, (MOD - 1) / n), i;
w[0][0] = w[1][0] = w[0][n] = w[1][n] = 1;
rep(i,1,n) w[0][i] = w[1][n - i] = w[0][i - 1] * G % MOD;
rep(i,0,n) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << k);
}
void fnt(ll *a, int f) {
int i, j, k;
rep(i,0,n) if (rev[i] < i) swap(a[i], a[rev[i]]);
for (i = 2; i <= n; i <<= 1)
for (j = 0; j < n; j += i)
rep(k, 0, i / 2) {
ll x = a[j + k], y = a[j + k + i / 2] * w[f][k * (n / i)] % MOD;
a[j + k] = (x + y) % MOD, a[j + k + i / 2] = (x - y + MOD) % MOD;
}
if (f) rep(i,0,n) a[i] = a[i] * inv_n % MOD;
}
void mul(ll *a, ll *b) {
int i;
rep(i,0,n) t[i] = b[i];
fnt(a, 0), fnt(t, 0);
rep(i,0,n) a[i] = a[i] * t[i] % MOD;
fnt(a, 1);
for(i=n-1;i>=m-1;--i) (a[i - m + 1] += a[i]) %= MOD, a[i] = 0;
}
} fnt;
int get_root(int m) {
static int g[N];
int i, j, t = m - 1, c = 0;
FOR(i,2,t) if (t % i == 0) {
g[c++] = i; while (t % i == 0) t /= i;
}
for (i = 2; ; ++i) {
rep(j,0,c) if (qpow(i, (m - 1) / g[j], m) == 1) break;
if (j == c) return i;
}
}
int h[N]; ll a[N], b[N];
int main() {
int i, e, n, m, x, s, g = 2;
scanf ("%d%d%d%d", &n, &m, &x, &s);
g = get_root(m);
int t = 1; h[1] = 0;
rep(i, 1, m - 1) t = t * g % m, h[t] = i;
fnt.init(m);
rep(i, 0, s) {
scanf("%d", &e);
if (e) a[h[e]] = 1;
}
for (b[0] = 1; n; fnt.mul(a, a), n >>= 1)
if (n & 1) fnt.mul(b, a);
printf("%lld", b[h[x]]);
return 0;
}
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中元素不重复