http://www.lydsy.com/JudgeOnline/problem.php?id=3992

这道题好难啊。

第一眼谁都能看出来是个dp,设\(f(i,j)\)表示转移到第i位时前i位的乘积模m等于j的方案数。

转移很显然啊\(f(i,j)=\sum_{x,y\in[0,m)}[xy\mod m=j]f(i-1,x)*f(i-1,y)\)。

这个下标是乘积取模的转移根本无法优化啊。

但注意到题目最下方说m是一个质数。。。

把x=0特判掉,剩下\(x\in[1,m-1)\)时把x转化为m的原根的幂次。

设m的原根为\(g_m\)。

那么\(f(i,g_m^j)=\sum_{x,y\in[0,m-1)}[(x+y)\mod m=j]f(i-1,g_m^x)*f(i-1,g_m^y)\)。

这样通过原根在[0,m-1)上的不重不漏的一一映射,乘积取模变成加法取模,化成了一个循环卷积的形式。

(话说看模数也知道是NTT啊qwq)循环卷积直接用NTT做就可以了。

但要做N次循环卷积,\(N\leq 10^9\)。。。

在外面套层快速幂就可以了O(∩_∩)O~~

快速幂套循环卷积的正确性?先不循环卷积然后再压成循环卷积就很好证明啊。不过也可以把快速幂看成一个倍增,每次合并两个dp数组之类的,正确性都显然啊qwq

注意数组不要开小!用于NTT的数组要开到2的幂次qwq

时间复杂度\(O(m^2+m\log m\log n)\)。

(看了Menci大大的博客,“把原根的幂次看成多项式的幂次,dp数组记录在系数里”这个东西还叫生成函数?)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll; const int M = 8193;
const int g = 267198;
const int p = 1004535809; int nin; int ipow(int a, int b) {
int ret = 1, w = a;
while (b) {
if (b & 1) ret = 1ll * ret * w % p;
w = 1ll * w * w % p;
b >>= 1;
}
return ret;
} int da[M << 1], db[M << 1], dc[M << 1], rev[M << 1], nWN[15], WN[15], n, m, x, s, C; void DNT(int *a, int *A, int flag) {
int tmp = 1;
for (int i = 0; i < n; ++i) A[rev[i]] = a[i];
for (int len = 2; len <= n; len <<= 1, ++tmp) {
int mid = len >> 1, wn = flag == 1 ? WN[tmp] : nWN[tmp];
for (int i = 0; i < n; i += len) {
int w = 1;
for (int j = 0; j < mid; ++j) {
int t = A[i + j], u = 1ll * A[i + j + mid] * w % p;
A[i + j] = (t + u) % p;
A[i + j + mid] = (t - u + p) % p;
w = 1ll * w * wn % p;
}
}
} if (flag == -1)
for (int i = 0; i < n; ++i)
A[i] = 1ll * A[i] * nin % p;
} int top; void NTTsqr(int *a) {
DNT(a, da, 1);
for (int i = 0; i < n; ++i)
da[i] = 1ll * da[i] * da[i] % p;
DNT(da, a, -1);
for (int i = 0; i < top; ++i) {
(a[i] += a[i + top]) %= p;
a[i + top] = 0;
}
} void NTT(int *a, int *b) {
DNT(a, da, 1); DNT(b, db, 1);
for (int i = 0; i < n; ++i)
dc[i] = 1ll * da[i] * db[i] % p;
DNT(dc, a, -1);
for (int i = 0; i < top; ++i) {
(a[i] += a[i + top]) %= p;
a[i + top] = 0;
}
} void init() {
int tot = 0, num = top << 1;
while (num) {num >>= 1; ++tot;}
n = 1 << tot;
nin = ipow(n, p - 2); int res;
for (int i = 0; i < n; ++i) {
num = i; res = 0;
for (int j = 0; j < tot; ++j) {
res <<= 1;
if (num & 1) res |= 1;
num >>= 1;
}
rev[i] = res;
} WN[14] = g, nWN[14] = ipow(g , p - 2);
for (int i = 13; i >= 1; --i) {
WN[i] = 1ll * WN[i + 1] * WN[i + 1] % p;
nWN[i] = 1ll * nWN[i + 1] * nWN[i + 1] % p;
}
} bool shown[M];
int r[M << 1], ww[M << 1], c[M]; int main() {
scanf("%d%d%d%d", &C, &m, &x, &s); top = m - 1;
if (x == 0) {printf("%d\n", (ipow(m, n) - ipow(m - 1, n) + p) % p); return 0;}
int num;
for (int i = 2; i < m; ++i) {
int ret = 1; bool flag = true;
for (int j = 0; j < top; ++j) {
ret = 1ll * ret * i % m;
if (shown[ret]) {flag = false; break;}
shown[ret] = true;
} if (!flag || ret != 1) {
ret = 1;
for (int j = 0; j < top; ++j) {
ret = 1ll * ret * i % m;
if (shown[ret]) shown[ret] = false;
else break;
}
} else {
num = i;
break;
}
} int ret = 1;
for (int i = 0; i < top; ++i) {
c[ret] = i;
ret = 1ll * ret * num % m;
} init(); int tt;
for (int i = 1; i <= s; ++i) {
scanf("%d", &tt);
if (tt != 0) ww[c[tt]] = 1;
} r[0] = 1;
while (C) {
if (C & 1) NTT(r, ww);
NTTsqr(ww);
C >>= 1;
} printf("%d\n", r[c[x]]); return 0;
}

【BZOJ 3993】【SDOI 2015】序列统计的更多相关文章

  1. [BZOJ 3992] [SDOI 2015] 序列统计(DP+原根+NTT)

    [BZOJ 3992] [SDOI 2015] 序列统计(DP+原根+NTT) 题面 小C有一个集合S,里面的元素都是小于质数M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数 ...

  2. BZOJ 3992 [SDOI 2015] 序列统计 解题报告

    这个题最暴力的搞法就是这样的: 设 $Dp[i][j]$ 为前 $i$ 个数乘积为 $j$ 的方案数. 转移的话就不多说了哈... 当前复杂度 $O(nm^2)$ 注意到,$M$ 是个质数,就说明 $ ...

  3. [BZOJ 3992] [SDOI 2015] 序列统计

    Description 传送门 Solution [一] 设 \(f[i][j]\) 表示前 \(i\) 个数的乘积在模 \(p\) 意义下等于 \(j\) 的方案数,有 \[ f[i][j]=\su ...

  4. BZOJ 3993 [SDOI 2015] 星际战争 解题报告

    首先我们可以二分答案. 假设当前二分出来的答案是 $Ans$ ,那么我们考虑用网络流检验: 设武器为 $X$,第 $i$ 个武器的攻击力为 $B_i$: 设机器人为 $Y$,第 $i$ 个机器人的装甲 ...

  5. [SDOI 2015]序列统计

    Description 题库链接 给出集合 \(S\) ,元素都是小于 \(M\) 的非负整数.问能够生成出多少个长度为 \(N\) 的数列 \(A\) ,数列中的每个数都属于集合 \(S\) ,并且 ...

  6. 【BZOJ 4403】 4403: 序列统计 (卢卡斯定理)

    4403: 序列统计 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 653  Solved: 320 Description 给定三个正整数N.L和R, ...

  7. [BZOJ 4818] [SDOI 2017] 序列计数

    Description Alice想要得到一个长度为 \(n\) 的序列,序列中的数都是不超过 \(m\) 的正整数,而且这 \(n\) 个数的和是 \(p\) 的倍数. Alice还希望,这 \(n ...

  8. 【BZOJ】3992: [SDOI2015]序列统计 NTT+生成函数

    [题意]给定一个[0,m-1]范围内的数字集合S,从中选择n个数字(可重复)构成序列.给定x,求序列所有数字乘积%m后为x的序列方案数%1004535809.1<=n<=10^9,3< ...

  9. BZOJ 3990 [SDOI 2015] 排序 解题报告

    这个题哎呀...细节超级多... 首先,我猜了一个结论.如果有一种排序方案是可行的,假设这个方案是 $S$ . 那么我们把 $S$ 给任意重新排列之后,也必然可以构造出一组合法方案来. 于是我们就可以 ...

  10. [BZOJ 3992][SDOI2015]序列统计

    3992: [SDOI2015]序列统计 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 2275  Solved: 1090[Submit][Stat ...

随机推荐

  1. Different Integers(牛客多校第一场+莫队做法)

    题目链接:https://www.nowcoder.com/acm/contest/139/J 题目: 题意:给你n个数,q次查询,对于每次查询得l,r,求1~l和r~n元素得种类. 莫队思路:1.将 ...

  2. 【转】MP3文件原理及结构解析

    1.引言文件压缩技术的日新月异使得MP3成为时下最烫手的音乐格式,优质的音乐随着0与1的排列迅速散布 到世界各地,撼动人心.何谓MP3?MP3的全称是MPEG Audio Layer 3,它是一种高效 ...

  3. 通过or注入py脚本

    代码思路 1.主要还是参考了别人的代码,确实自己写的和别人写的出路很大,主要归咎还是自己代码能力待提高吧. 2.将功能集合成一个函数,然后通过*args这个小技巧去调用.函数的参数不是argv的值,但 ...

  4. python基础===requests学习笔记

    这里有一个新的学习requests网站:http://docs.python-requests.org/zh_CN/latest/user/quickstart.html2017/11/30 Requ ...

  5. JS常用操作方法

    1.splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目. 注释:该方法会改变原始数组. 1 <script type="text/javascript"& ...

  6. Makefile系列之二 : 命令

    一.显示命令 echo “@”字符可以控制命令是否在屏幕上显示,如 @echo 正在编译XXX模块......  输出: 正在编译XXX模块...... 如果没有“@"则输出 : echo ...

  7. tcpcopy 流量复制

    依赖 libpcap 线上服务器安装 https://github.com/session-replay-tools/tcpcopy.git 下载后解压 #--pcap-capture 默认raw s ...

  8. ORACLE导入Excel数据

    首先建好一个和Excel表字段对应字段的表,然后 select t.* from 表名 t  for update; 点击这个锁子,打开它 粘贴,然后 再提交事务即可

  9. Rsync文件同步服务

    Rsync简介 Rsync是一款开源的.快速的.多功能的.可实现全量及增量的本地或远程数据同步备份的优秀工具,适用于Unix/Linux/Windows等多种操作系统. Rsync的特性 支持拷贝特殊 ...

  10. Redis -- 数据类型小结

    redis key 的命名规则: 对象类型:对象id:对象属性  (hset car:1: price 500.hset car:1: name:tom) 一.redis 数据类型: 1. 字符串类型 ...