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;
}
原文:http://www.cnblogs.com/ruoruoruo/p/7784728.html