BZOJ3992: [SDOI2015]序列统计(NTT 原根 生成函数)
题意
给出大小为\(S\)的集合,从中选出\(N\)个数,满足他们的乘积\(\% M = X\)的方案数
Sol
神仙题Orz
首先不难列出最裸的dp方程,设\(f[i][j]\)表示选了\(i\)个数,他们的乘积为\(j\)的方案数
设\(g[k] = [\exists a_i = k]\)
转移的时候
\]
不难发现每次的转移都是相同的,因此可以直接矩阵快速幂,时间复杂度变为\(logN M^2\)
观察上面的式子,如果我们能把\((j * k) \% M\),变成\((j + k) \% M\)的话,就是一个循环卷积的形式了
这里可以用原根来实现,设\(g\)表示\(M\)的原根,\(mp[i] = j\)表示\(g^j = i\)
直接对每个物品构造生成函数,利用mp转移即可
因为转移是个循环卷积,所以统计答案的时候应该把第\(i\)项和第\(i+m-1\)项的系数加起来
至于为啥只统计一项。

#include<bits/stdc++.h>
using namespace std;
const int mod = 1004535809, G = 3, Gi = 334845270, MAXN = 1e5 + 10;
inline int read() {
char c = getchar(); int x = 0, f = 1;
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int N, M, X, S;
int r[MAXN], lim, L, ind[MAXN], s[MAXN], f[MAXN], a[MAXN], b[MAXN];
int mul(int a, int b) {
return 1ll * a * b % mod;
}
int add(int x, int y) {
if(x + y < 0) return x + y + mod;
return x + y >= mod ? x + y - mod : x + y;
}
int dec(int x, int y) {
return x - y < 0 ? x - y + mod : x - y;
}
int fp(int a, int p, int mod) {
int base = 1;
while(p) {
if(p & 1) base = 1ll * base * a % mod;
a = 1ll * a * a % mod; p >>= 1;
}
return base;
}
int GetG(int x) {
static int q[MAXN]; int tot = 0, tp = x - 1;
for(int i = 2; i * i <= tp; i++) {
if(!(tp % i)) {
q[++tot] = i;
while(!(tp % i)) tp /= i;
}
}
if(tp > 1) q[++tot] = tp;
for(int i = 2, j; i <= x - 1; i++) {
for(j = 1; j <= tot; j++) if(fp(i, (x - 1) / q[j], x) == 1) break;
if(j == tot + 1) return i;
}
}
void NTT(int *a, int N, int type) {
for(int i = 1; i < N; i++) if(i < r[i]) swap(a[i], a[r[i]]);
for(int mid = 1; mid < N; mid <<= 1) {
int R = mid << 1, Wn = fp(type == 1 ? G : Gi, (mod - 1) / R, mod);
for(int j = 0; j < lim; j += R) {
for(int w = 1, k = 0; k < mid; k++, w = mul(w, Wn)) {
int x = a[j + k], y = mul(w, a[j + k + mid]);
a[j + k] = add(x, y);
a[j + k + mid] = dec(x, y);
}
}
}
if(type == -1) {
for(int i = 0, inv = fp(lim, mod - 2, mod); i < N; i++) a[i] = mul(a[i], inv);
}
}
void mul(int *a1, int *b1, int *c) {
memset(a, 0, sizeof(a)); memset(b, 0, sizeof(b));//tag
for(int i = 0; i < M - 1; i++) a[i] = a1[i], b[i] = b1[i];
NTT(a, lim, 1); NTT(b, lim, 1);
for(int i = 0; i < lim; i++) a[i] = mul(a[i], b[i]);
NTT(a, lim, -1);
for(int i = 0; i < M - 1; i++) c[i] = add(a[i], a[i + M - 1]);
}
void Pre() {
lim = 1;
while(lim <= 2 * (M - 2)) lim <<= 1, L++;
for(int i = 0; i < lim; i++) r[i] = (r[i >> 1] >> 1) | (i & 1) << (L - 1);
int d = GetG(M);
for(int i = 0; i < M - 1; i++) ind[fp(d, i, M)] = i;
}
int main() {
N = read(); M = read(); X = read(); S = read();
Pre();
for(int i = 1; i <= S; i++) {
int x = read();
if(x) f[ind[x]]++;
}
s[ind[1]] = 1;
while(N) {
if(N & 1) mul(s, f, s);
mul(f, f, f); N >>= 1;
}
printf("%d", s[ind[X]]);
return 0;
}
/*
40000000 3 1 2
1 2
4 3 1 2
1 2
*/
BZOJ3992: [SDOI2015]序列统计(NTT 原根 生成函数)的更多相关文章
- [BZOJ3992][SDOI2015]序列统计(DP+原根+NTT)
3992: [SDOI2015]序列统计 Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 1888 Solved: 898[Submit][Statu ...
- bzoj 3992: [SDOI2015]序列统计【原根+生成函数+NTT+快速幂】
还是没有理解透原根--题目提示其实挺明显的,M是质数,然后1<=x<=M-1 这种计数就容易想到生成函数,但是生成函数是加法,而这里是乘法,所以要想办法变成加法 首先因为0和任何数乘都是0 ...
- bzoj 3992: [SDOI2015]序列统计 NTT+原根
今天开始学习丧心病狂的多项式qaq...... . code: #include <bits/stdc++.h> #define ll long long #define setIO ...
- 【BZOJ3992】[SDOI2015]序列统计 NTT+多项式快速幂
[BZOJ3992][SDOI2015]序列统计 Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属 ...
- [SDOI2015]序列统计(NTT+求原根)
题目 [SDOI2015]序列统计 挺好的题!!! 做法 \(f[i][j]\)为第\(i\)个数前缀积在模\(M\)意义下为\(j\) 显然是可以快速幂的:\[f[2*i][j]=\sum\limi ...
- BZOJ 3992: [SDOI2015]序列统计 NTT+快速幂
3992: [SDOI2015]序列统计 Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 1155 Solved: 532[Submit][Statu ...
- 2018.12.31 bzoj3992: [SDOI2015]序列统计(生成函数+ntt+快速幂)
传送门 生成函数简单题. 题意:给出一个集合A={a1,a2,...as}A=\{a_1,a_2,...a_s\}A={a1,a2,...as},所有数都在[0,m−1][0,m-1][0,m− ...
- 【BZOJ】3992: [SDOI2015]序列统计 NTT+生成函数
[题意]给定一个[0,m-1]范围内的数字集合S,从中选择n个数字(可重复)构成序列.给定x,求序列所有数字乘积%m后为x的序列方案数%1004535809.1<=n<=10^9,3< ...
- BZOJ3992 [SDOI2015]序列统计 【生成函数 + 多项式快速幂】
题目 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数 列,数列中的每个数都属于集合S.小C用这个生成器生成了许多这样的数列.但是小C有一个问题 ...
随机推荐
- Flink学习笔记:Time的故事
本文为<Flink大数据项目实战>学习笔记,想通过视频系统学习Flink这个最火爆的大数据计算框架的同学,推荐学习课程: Flink大数据项目实战:http://t.cn/EJtKhaz ...
- P5108 仰望半月的夜空 SAM+线段树覆盖
$ \color{#0066ff}{ 题目描述 }$ 半月的夜空中,寄托了多少人与人之间的思念啊 曦月知道,这些思念会汇集成一个字符串\(S(n = |S|)\) 由于思念汇集的过于复杂,因此曦月希望 ...
- Linux进程间通信——使用System V 消息队列
消息队列 消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法. 每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构.我们可以通过发送消息来避免命名管道的同步和阻塞问 ...
- 洛谷 P4307 [JSOI2009]球队收益 / 球队预算(最小费用最大流)
题面 luogu 题解 最小费用最大流 先假设剩下\(m\)场比赛,双方全输. 考虑\(i\)赢一局的贡献 \(C_i*(a_i+1)^2+D_i*(b_i-1)^2-C_i*a_i^2-D_i*b_ ...
- 2018牛客多校6 - I Team Rocket KD树维护空间
题意:给出n条铁路区间\([L,R]\),共有m个boom依时间顺序放置在\(k_i\)中,区间与\(k_i\)有交集的都被炸掉 求每次炸掉的铁路个数和最后输出所有id被炸的时间点 炸弹能炸到的区间满 ...
- git特殊用法
git stash的使用 1.当前分支写了一半的代码,需要切到其他分支修复bug或者完成优先级较高的任务时 git stash 暂存分支进度 git stash list 查看草稿区 git stas ...
- 服务端模拟http服务请求客户端
try { InputStream in = req.getInputStream(); int i = -1; ByteArrayOutputStream out = new ByteArrayOu ...
- Centos 7搭建Gitlab服务器(一),搭配文章(二)一起使用,效果更好
一. 安装并配置必要的依赖关系在CentOS系统上安装所需的依赖:ssh,防火墙,postfix(用于邮件通知) ,wget,以下这些命令也会打开系统防火墙中的HTTP和SSH端口访问. 1.安装ss ...
- python爬虫专栏学习
知乎的一个讲python的专栏,其中爬虫的几篇文章,偏入门解释,快速看了一遍. 入门 爬虫基本原理:用最简单的代码抓取最基础的网页,展现爬虫的最基本思想,让读者知道爬虫其实是一件非常简单的事情. 爬虫 ...
- 同时安装Python2和Python3,如何兼容并切换使用详解
由于历史原因,Python有两个大的版本分支,Python2和Python3,又由于一些库只支持某个版本分支,所以需要在电脑上同时安装Python2和Python3,因此如何让两个版本的Python兼 ...