[BZOJ3992][SDOI2015]序列统计
Posted Elder_Giang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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
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; }
以上是关于[BZOJ3992][SDOI2015]序列统计的主要内容,如果未能解决你的问题,请参考以下文章