【LG3321】[SDOI2015]序列统计

题面

洛谷

题解

前置芝士:原根

我们先看一下对于一个数\(p\),它的原根\(g\)有什么性质(好像就是定义):

\(g^0\%p,g^1\%p,g^2\%p...g^{p-2}\%p\)恰好等于\([0,p - 1]\)中所有数。

那么怎么求呢?

对\(\varphi(p)\)分解质因数,得到\(\varphi(p)=p_1^{a_1}p_2^{a_2}p_3^{a_3}...p_n^{a_n}\)

从\(2\)~\((p-1)\)枚举\(g\),如果满足\(g\)对于\(\forall p_i\),有$$g^{\frac {\varphi(p)}{p_i}}\neq1;mod;p$$

则该数是个原根,\(break\),否则\(continue\)

关于此题:

有了上面的铺垫,我们想一想这题怎么做。

设\(f[i][j]\)表示选了\(i\)个数,乘积\(\%m\)为\(j\)的方案数,

则有转移:

\[f[2*i][c]=\sum_{a*b\%m=c}f[i][a]*f[i][b]
\]

这时候我们复杂度是\(O(m^2\log n)\)的,跑不过去,而转移次数已经无法优化了,想办法优化转移。

观察这个转移,如果它的判断条件为\((a+b)\%m=c\),我们不就可以卷起来了吗?

想想什么能把乘法换成加法?对数!!!

但是因为是模意义下的对数,所以我们取个原根就行了。

\(\therefore\) 设\(C=\log_gc\%m,A=\log_ga\%m,B=\log_gb\%m\)。

则有转移:

\[f[2*i][C]=\sum_{(A+B)\%\varphi (m)=c}f[i][A]*f[i][B]
\]

那么就可以用\(NTT\)搞了,

注意最后要\(f[i][j]+=f[i][j+\varphi (m)]\),因为你每次卷起来后\(\varphi (m)\)~\((2\varphi (m)-2)\)项都是要算贡献的。

注意:集合中\(\%m=0\)的数也要判一下。

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
using namespace std;
inline int gi() {
register int data = 0, w = 1;
register char ch = 0;
while (!isdigit(ch) && ch != '-') ch = getchar();
if (ch == '-') w = -1, ch = getchar();
while (isdigit(ch)) data = 10 * data + ch - '0', ch = getchar();
return w * data;
}
int fpow(int x, int y, int mod) {
int res = 1;
while (y) {
if (y & 1) res = 1ll * res * x % mod;
x = 1ll * x * x % mod;
y >>= 1;
}
return res;
}
const int Mod = 1004535809, G = 3, iG = fpow(G, Mod - 2, Mod);
int GetRoot(int x) {
int fact[10000], tot = 0;
int phi = x - 1;
for (int i = 2; i * i <= phi; i++) {
if (phi % i == 0) {
fact[++tot] = i;
while (phi % i == 0) phi /= i;
}
}
if (phi > 1) fact[++tot] = phi;
phi = x - 1;
for (int i = 2; i <= phi; i++) {
bool flg = 1;
for (int j = 1; j <= tot && flg; j++)
if (fpow(i, phi / fact[j], x) == 1) flg = 0;
if (flg) return i;
}
return -1;
}
const int MAX_M = 2.4e4 + 5;
int Limit, rev[MAX_M];
void NTT(int *p, int op) {
for (int i = 0; i < Limit; i++) if (i < rev[i]) swap(p[i], p[rev[i]]);
for (int i = 1; i < Limit; i <<= 1) {
int rot = fpow(op == 1 ? G : iG, (Mod - 1) / (i << 1), Mod);
for (int j = 0; j < Limit; j += (i << 1)) {
int w = 1;
for (int k = 0; k < i; k++, w = 1ll * w * rot % Mod) {
int x = p[j + k], y = 1ll * w * p[i + k + j] % Mod;
p[j + k] = (x + y) % Mod, p[i + j + k] = (x - y + Mod) % Mod;
}
}
}
if (op == -1) {
int inv = fpow(Limit, Mod - 2, Mod);
for (int i = 0; i < Limit; i++) p[i] = 1ll * p[i] * inv % Mod;
}
}
map<int, int> mp;
int N, M, X, S, F[MAX_M], H[MAX_M];
void mul(int *A, int *B, int *C) {
static int res[MAX_M], a[MAX_M], b[MAX_M];
for (int i = 0; i < Limit; i++) a[i] = A[i], b[i] = B[i];
NTT(a, 1), NTT(b, 1);
for (int i = 0; i < Limit; i++) a[i] = 1ll * a[i] * b[i] % Mod;
NTT(a, -1);
for (int i = 0; i < M - 1; i++) res[i] = (a[i] + a[i + M - 1]) % Mod;
for (int i = 0; i < M - 1; i++) C[i] = res[i];
}
int main () {
N = gi(), M = gi(), X = gi(), S = gi();
int g = GetRoot(M); for (int i = 0; i < M - 1; i++) mp[fpow(g, i, M)] = i;
for (int i = 1, x; i <= S; i++) {
x = gi() % M;
if (x) F[mp[x % M]]++;
}
H[mp[1]] = 1;
int p = 0;
for (Limit = 1; Limit <= 2 * M; Limit <<= 1, ++p) ;
for (int i = 0; i < Limit; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (p - 1));
while (N) {
if (N & 1) mul(H, F, H);
mul(F, F, F);
N >>= 1;
}
printf("%d\n", H[mp[X]]);
return 0;
}

【LG3321】[SDOI2015]序列统计的更多相关文章

  1. BZOJ 3992: [SDOI2015]序列统计 [快速数论变换 生成函数 离散对数]

    3992: [SDOI2015]序列统计 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 1017  Solved: 466[Submit][Statu ...

  2. [SDOI2015]序列统计

    [SDOI2015]序列统计 标签: NTT 快速幂 Description 给你一个模m意义下的数集,需要用这个数集生成一个数列,使得这个数列在的乘积为x. 问方案数模\(1004535809\). ...

  3. 3992: [SDOI2015]序列统计

    3992: [SDOI2015]序列统计 链接 分析: 给定一个集和s,求多少个长度为n的序列,满足序列中每个数都属于s,并且所有数的乘积模m等于x. 设$f=\sum\limits_{i=0}^{n ...

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

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

  5. [BZOJ3992][SDOI2015]序列统计(DP+原根+NTT)

    3992: [SDOI2015]序列统计 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 1888  Solved: 898[Submit][Statu ...

  6. 【BZOJ3992】[SDOI2015]序列统计 NTT+多项式快速幂

    [BZOJ3992][SDOI2015]序列统计 Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属 ...

  7. BZOJ 3992: [SDOI2015]序列统计 NTT+快速幂

    3992: [SDOI2015]序列统计 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 1155  Solved: 532[Submit][Statu ...

  8. BZOJ 3992: [SDOI2015]序列统计 快速幂+NTT(离散对数下)

    3992: [SDOI2015]序列统计 Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S ...

  9. [SDOI2015]序列统计(NTT+求原根)

    题目 [SDOI2015]序列统计 挺好的题!!! 做法 \(f[i][j]\)为第\(i\)个数前缀积在模\(M\)意义下为\(j\) 显然是可以快速幂的:\[f[2*i][j]=\sum\limi ...

随机推荐

  1. oracle exp 无法导出空表

    oracle exp 无法导出空表   select 'alter table '|| a.table_name ||' allocate extent;' from user_tables a wh ...

  2. 【[SCOI2009]迷路】

    大水题一遍 过掉比较繁琐的拆点还是非常开心的 发现每一条边的边权可能不是\(1\),但是边权的范围非常小,同时点数也非常小,只有\(n<=10\),所以我们可以将一个点拆成九个点,之后随便一连边 ...

  3. luogu P1272 重建道路

    嘟嘟嘟 这好像是一种树上背包. 我们令dp[i][j] 表示在 i 所在的子树中(包括节点 i)分离出一个大小为 j 的子树最少需割多少条边. 那么转移方程就是 dp[u][j] = min(dp[u ...

  4. JAVA:字符串反转

    import java.util.ArrayList;import java.util.Arrays;import java.util.Collections;import java.util.Lis ...

  5. 【luogu P2245 星际导航】 题解

    题目链接:https://www.luogu.org/problemnew/show/P2245 = 货车运输 被逼着写过mst+lca 后来成了mst+树剖 #include <cstdio& ...

  6. python sorted() count() set(list)-去重 -- search + match

    2.用python实现统计一篇英文文章内每个单词的出现频率,并返回出现频率最高的前10个单词及其出现次数,并解答以下问题?(标点符号可忽略) (1) 创建文件对象f后,解释f的readlines和xr ...

  7. ASP.NET Core 中的 WebSocket 支持(转自MSDN)

    本文介绍 ASP.NET Core 中 WebSocket 的入门方法. WebSocket (RFC 6455) 是一个协议,支持通过 TCP 连接建立持久的双向信道. 它用于从快速实时通信中获益的 ...

  8. Java面向对象的三个特征

    首先,Java面向对象的三大特征: 三大特征: ▪ 封装 ▪ 继承 ▪ 多态 首先面向对象的第一个特性 封装 : 封装:就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操 ...

  9. Oracle 数据库数据结构(包括存储过程,函数,表,触发器等)版本控制器

    原理: 写系统触发器,在修改数据库结构的时候,把DDL写入表中 create sequence A_Ver_Control_seq minvalue nomaxvalue start incremen ...

  10. ios开发遇到的问题

    运行后界面空白,Xcode跳转到APPDelegate.swift文件提示如下 第一种可能原因: 做输出口后在代码中重新命名了输出口 解决方法: 右键控件关闭输出口的连接,变回+号,将它重新连到代码的 ...