P3321 [SDOI2015]序列统计 FFT+快速幂+原根
\(\color{#0066ff}{ 题目描述 }\)
小C有一个集合S,里面的元素都是小于M的非负整数。他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S。小C用这个生成器生成了许多这样的数列。但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中所有数的乘积mod M的值等于x的不同的数列的有多少个。小C认为,两个数列{Ai}和{Bi}不同,当且仅当至少存在一个整数i,满足Ai≠Bi。另外,小C认为这个问题的答案可能很大,因此他只需要你帮助他求出答案mod 1004535809的值就可以了。
\(\color{#0066ff}{输入格式}\)
一行,四个整数,N、M、x、|S|,其中|S|为集合S中元素个数。第二行,|S|个整数,表示集合S中的所有元素。
\(\color{#0066ff}{输出格式}\)
一行,一个整数,表示你求出的种类数mod 1004535809的值。
\(\color{#0066ff}{输入样例}\)
4 3 1 2
1 2
\(\color{#0066ff}{输出样例}\)
8
\(\color{#0066ff}{数据范围与提示}\)
对于10%的数据,1<=N<=1000;
对于30%的数据,3<=M<=100;
对于60%的数据,3<=M<=800;
对于全部的数据,1<=N<=10^9,3<=M<=8000,M为质数,1<=x<=M-1,输入数据保证集合S中元素不重复
\(\color{#0066ff}{ 题解 }\)
乘法在这里很不好处理
m为质数又很小,这让我们可以想到原根
如果在mod m意义下,g的0--->m-2次幂mod m各不相同,那么g就称作m的一个原根
质数一定有原根
有关原根,详见巨佬blog
这样乘法就被转化为了加法
直接FFT套快速幂 ,注意和是在mod(m-2)意义下的,所以统计的时候要把后面的都算回来
#include<bits/stdc++.h>
#define LL long long
LL in() {
char ch; LL x = 0, f = 1;
while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
return x * f;
}
const int maxn = 1e5 + 100;
const int mod = 1004535809;
using std::vector;
int r[maxn], len;
int a[maxn], b[maxn], root, mp[maxn];
LL ksm(LL x, LL y, LL z) {
LL re = 1LL;
while(y) {
if(y & 1) re = re * x % z;
x = x * x % z;
y >>= 1;
}
return re;
}
void FNTT(vector<int> &A, int flag) {
A.resize(len);
for(int i = 0; i < len; i++) if(i < r[i]) std::swap(A[i], A[r[i]]);
for(int l = 1; l < len; l <<= 1) {
int w0 = ksm(3, (mod - 1) / (l << 1), mod);
for(int i = 0; i < len; i += (l << 1)) {
int w = 1, a0 = i, a1 = i + l;
for(int k = 0; k < l; k++, a0++, a1++, w = 1LL * w0 * w % mod) {
int tmp = 1LL * A[a1] * w % mod;
A[a1] = ((A[a0] - tmp) % mod + mod) % mod;
A[a0] = (A[a0] + tmp) % mod;
}
}
}
if(!(~flag)) {
std::reverse(A.begin() + 1, A.end());
int inv = ksm(len, mod - 2, mod);
for(int i = 0; i < len; i++) A[i] = 1LL * A[i] * inv % mod;
}
}
vector<int> work(vector<int> A, vector<int> B, int m) {
A.resize(m - 1), B.resize(m - 1);
int tot = A.size() + B.size() - 1;
for(len = 1; len <= tot; len <<= 1);
for(int i = 0; i < len; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) * (len >> 1));
FNTT(A, 1), FNTT(B, 1);
vector<int> tmp;
for(int i = 0; i < len; i++) tmp.push_back(1LL * A[i] * B[i] % mod);
FNTT(tmp, -1);
vector<int> ans;
ans.resize(m - 1);
for(int i = 0; i < len; i++) (ans[i % (m - 1)] += tmp[i]) %= mod;
return ans;
}
int getroot(int m) {
for(int i = 2; i < m; i++) {
int res = m - 1;
for(int j = 2; j <= res; j++) {
if(res % j == 0) {
while(res % j == 0) res /= j;
if(ksm(i, (m - 1) / j, m) == 1) break;
}
if(j >= res) return i;
}
}
return 233;
}
vector<int> ksm(vector<int> A, int b, int m) {
vector<int> ans;
ans.push_back(1);
while(b) {
if(b & 1) ans = work(ans, A, m);
A = work(A, A, m);
b >>= 1;
}
return ans;
}
int main() {
int _ = in(), m = in(), x = in(), num = in(), n = 0;
for(int i = 1; i <= num; i++) {
int t = in();
if(t) a[++n] = t;
}
root = getroot(m);
for(int i = 0; i <= m - 2; i++) mp[ksm(root, i, m)] = i;
for(int i = 1; i <= n; i++) b[i] = mp[a[i]];
int xx = 0;
for(int i = 0; i < m; i++) if(ksm(root, i, m) == x % m) { xx = i; break; }
vector<int> f;
f.resize(m - 1);
for(int i = 1; i <= n; i++) f[b[i]] = 1;
f = ksm(f, _, m);
printf("%d\n", f[xx] % mod);
return 0;
}
P3321 [SDOI2015]序列统计 FFT+快速幂+原根的更多相关文章
- BZOJ 3992: [SDOI2015]序列统计 NTT+快速幂
3992: [SDOI2015]序列统计 Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 1155 Solved: 532[Submit][Statu ...
- [SDOI2015]序列统计(多项式快速幂)
题目描述 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S.小C用这个生成器生成了许多这样的数列.但是小C有一个问 ...
- Luogu P3321 [SDOI2015]序列统计
一道不错的多项式好题.还涉及了一些数论内容. 首先我们看到题目是求乘积模\(m\)的方案数,考虑到这种方案数我们一般都可以用生成函数来做. 但显然卷积的下标有加(FFT,NTT等)有位运算(FWT)但 ...
- [洛谷P3321][SDOI2015]序列统计
题目大意:给你一个集合$n,m,x,S(S_i\in(0,m],m\leqslant 8000,m\in \rm{prime},n\leqslant10^9)$,求一个长度为$n$的序列$Q$,满足$ ...
- P3321 [SDOI2015]序列统计
思路 首先有个挺显然的DP \[ dp[i][(j*k)\%m]+=dp[i-1][j]\times dp[i-1][k] \] 想办法优化这个DP 这个dp也可以写成这样 \[ dp[i][j]=\ ...
- 洛咕 P3321 [SDOI2015]序列统计
显然dp就是设\(f[i][j]\)表示dp了i轮,对m取膜是j的方案数 \(f[i][xy\mod m]=f[i-1][x]\times f[i-1][y]\) 这是\(O(nm^2)\)的 像我这 ...
- 洛谷P3321 [SDOI2015]序列统计(NTT)
传送门 题意:$a_i\in S$,求$\prod_{i=1}^na_i\equiv x\pmod{m}$的方案数 这题目太珂怕了……数学渣渣有点害怕……kelin大佬TQL 设$f[i][j]$表示 ...
- 【BZOJ3992】[SDOI2015]序列统计 NTT+多项式快速幂
[BZOJ3992][SDOI2015]序列统计 Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属 ...
- BZOJ 3992: [SDOI2015]序列统计 快速幂+NTT(离散对数下)
3992: [SDOI2015]序列统计 Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S ...
随机推荐
- jenkins学习 02 jenkins安装与配置
1: Jenkins安装 在最简单的情况下,Jenkins 只需要两个步骤: (1)下载最新的版本(一个 WAR 文件).Jenkins官方网址: http://Jenkins-ci.org/ (2 ...
- IOCP编程原理(转)
在我的博客之前写了很多关于IOCP的“行云流水”似的看了让人发狂的文章,尤其是几篇关于 IOCP加线程池文章,更是让一些功力不够深厚的初学IOCP者,有种吐血的感觉.为了让大家能够立刻提升内力修为,并 ...
- Android 4学习(1):学习路线图
学习路线图 如下图所示,整个Android的架构可以分为四层,五个部分.我给自己制定的学习路线图是这样的: 对于有java基础的入门级android开发者而言,首先要学会使用Application F ...
- eclipse配置hadoop location的端口号
在eclipse下配置hadoop location的时候 hadoop端口号应该与conf文件夹下的core-site.xml以及mapred-site.xml保持一致 前者对应dfs master ...
- 提示crontab command not found的解决方法
操作步骤 1. 确认crontab是否安装: 执行 crontab 命令如果报 command not found,就表明没有安装 2. 安装 crontab 执行 yum insta ...
- day17-jdbc 4.DriverManager详解
我们找的不是J2EE的API,因为JDBC不在web程序用也可以,纯java也能玩.所以jdbc的api在java se那里,或者说是在jdk那里. DriverManger它底层有一个集合是一个Ve ...
- [patl2-018]多项式A除以B
解题关键:多项式除法的模拟. #include<cstdio> #include<cstring> #include<algorithm> #include< ...
- __call()和__callStatic()方法
__call() 当对象访问不存在的方法时,__call()方法会被自动调用__callStatic() 当对象访问不存在的静态方法时,__callStatic()方法会被自动调用 这两个方法在PHP ...
- 771. Jewels and Stones珠宝数组和石头数组中的字母对应
[抄题]: You're given strings J representing the types of stones that are jewels, and S representing th ...
- Mind Map - FreeMind
FreeMind[1]是一款基于java的免费的脑图(mind mapping)制作与管理软件.FreeMind开发项目组正致力于使其成为一款高效率的工具.FreeMind具有一键“展开/折叠”功能以 ...