3992: [SDOI2015]序列统计

链接

分析:

  给定一个集和s,求多少个长度为n的序列,满足序列中每个数都属于s,并且所有数的乘积模m等于x。

  设$f=\sum\limits_{i=0}^{n - 1} a_i x ^ i \ \ 如果集合中存在i,a_i = 1$

  那么答案的生成函数为f自乘n次,这里可以快速幂。这里"乘法"定义是:设多项式a乘多项式b等于c,$\sum\limits_{k=0}^{n - 1} c_k = \sum\limits_{i \times j = k} a_i \times b_j$ 每次“乘法”的复杂度是$m^2$,所以复杂度是$O(m^2logn)$。

  考虑优化“乘法”的部分,我们知道多项式乘法利用FFT/NTT可以做到$nlogn$的,看能否转化为多项式乘法,即多项式乘法的定义变为$\sum\limits_{k=0}^{n - 1} c_k = \sum\limits_{i + j = k} a_i \times b_j$。

  NTT中,有引入原根的概念,在NTT中,原根的用途相当于单位根。 原根有一个性质:对于mod p下的原根g,$g^1, g^2 \dots g^{p - 1}$互不相同,$g^{p - 1} \equiv 1 \mod p$。而且$g^1, g^2 \dots g^{p - 1}$可以分别表示$1,2 \dots p - 1$。

  那么我们对m求出单位根,集合S中出现的每个数,都可以表示为$s_i = g^{t_{s_i}}$

  此时对于原来的一个序列y,$\prod y_i = x \mod m$,就变成了$\prod g ^{t_{y_i}} = g^{t_x} \mod m$,即$\sum t_{y_i} = x \mod m - 1$

  现在我们求的就是长度为n的序列,序列中每个数都属于集合t,并且所有数的和模(m-1)等于x 如此按照上面的做法,将乘法的定义改为多项式乘法的定义,快速幂+NTT即可复杂度$mlogmlogn$。

  注意:多项式乘法中是没有取模的,而这里(i+j)%(m-1),直接将数组加倍,然后NTT完后,大于等于m的加到相应的模m后的位置上即可。

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cctype>
#include<set>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long LL; inline int read() {
int x=,f=;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-;
for(;isdigit(ch);ch=getchar())x=x*+ch-'';return x*f;
} const int mod = ;
const int N = ;
int vis[N], rev[N], n = , m;
int f[N], g[N], a[N], b[N], inv; int ksm(int a,int b,int p) {
a %= p;
int ans = ;
while (b) {
if (b & ) ans = 1ll * ans * a % p;
a = 1ll * a * a % p;
b >>= ;
}
return ans % p;
}
int Calc(int x) {
if (x == ) return ;
for (int i = ; ; ++i) {
bool flag = ;
for (int j = ; j * j < x; ++j)
if (ksm(i, (x - ) / j, x) == ) { flag = false; break; }
if (flag) return i;
}
}
void NTT(int *a,int n,int ty) {
for (int i = ; i < n; ++i) if (i < rev[i]) swap(a[i], a[rev[i]]);
for (int m = ; m <= n; m <<= ) {
int w1 = ksm(, (mod - ) / m, mod);
if (ty == -) w1 = ksm(w1, mod - , mod);
for (int i = ; i < n; i += m) {
int w = ;
for (int k = ; k < (m >> ); ++k) {
int u = a[i + k], t = 1ll * w * a[i + k + (m >> )] % mod;
a[i + k] = (u + t) % mod;
a[i + k + (m >> )] = (u - t + mod) % mod;
w = 1ll * w * w1 % mod;
}
}
}
}
void mul(int *g,int *f) {
for (int i = ; i < n; ++i) a[i] = g[i] % mod, b[i] = f[i] % mod;
NTT(a, n, );
NTT(b, n, );
for (int i = ; i < n; ++i) a[i] = 1ll * a[i] * b[i] % mod;
NTT(a, n, -);
for (int i = ; i < n; ++i) a[i] = 1ll * a[i] * inv % mod;
for (int i = ; i < m - ; ++i) g[i] = (a[i] + a[i + m - ]) % mod;
}
void solve(int b) {
inv = ksm(n, mod - , mod);
g[] = ;
while (b) {
if (b & ) mul(g, f);
b >>= ;
mul(f, f);
}
}
int main() {
int cnt = read(); m = read(); int x = read(), s = read();
for (int i = ; i <= s; ++i) vis[read()] = ;
int q = Calc(m), pos = -, L = ;
for (int i = , j = ; i < m - ; ++i, j = 1ll * j * q % m) {
if (vis[j]) f[i] = ;
if (j == x) pos = i;
}
int M = (m - ) * ;
while (n < M) n <<= , L ++;
for (int i = ; i < n; ++i) rev[i] = (rev[i >> ] >> ) | ((i & ) << (L - ));
solve(cnt);
if (pos != -) cout << g[pos] % mod;
else cout << ;
return ;
}

3992: [SDOI2015]序列统计的更多相关文章

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

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

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

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

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

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

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

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

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

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

  6. bzoj 3992 [SDOI2015]序列统计——NTT(循环卷积&&快速幂)

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3992 有转移次数.模M余数.方案数三个值,一看就是系数的地方放一个值.指数的地方放一个值.做 ...

  7. bzoj 3992 [SDOI2015] 序列统计 —— NTT (循环卷积+快速幂)

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3992 (学习NTT:https://riteme.github.io/blog/2016-8 ...

  8. BZOJ.3992.[SDOI2015]序列统计(DP NTT 原根)

    题目链接 \(Description\) 给定\(n,m,x\)和集合\(S\).求\(\prod_{i=1}^na_i\equiv x\ (mod\ m)\)的方案数.其中\(a_i\in S\). ...

  9. bzoj 3992: [SDOI2015]序列统计【原根+生成函数+NTT+快速幂】

    还是没有理解透原根--题目提示其实挺明显的,M是质数,然后1<=x<=M-1 这种计数就容易想到生成函数,但是生成函数是加法,而这里是乘法,所以要想办法变成加法 首先因为0和任何数乘都是0 ...

随机推荐

  1. Java 使用stringTemplate导出大批量数据excel(百万级)

    目前java框架中能够生成excel文件的的确不少,但是,能够生成大数据量的excel框架,我倒是没发现,一般数据量大了都会出现内存溢出,所以,生成大数据量的excel文件要返璞归真,用java的基础 ...

  2. [UI] 精美UI界面欣赏[3]

    精美UI界面欣赏[3]

  3. 使用AKLocationManager定位

    使用AKLocationManager定位 https://github.com/ideaismobile/AKLocationManager 以下是使用情况: 是不是很简单呢,我们可以将它的步骤进一 ...

  4. 多线程应用-函数方式(thread)

    多线程只能使用一颗CPU,无法发挥多核心的优势.计算密集型用python的多线程效果不明显的,I/O密集型才能看出效果,可以发挥多核优势. GIL是全局资源锁,所以,如果没有涉及到资源的调用,是不会体 ...

  5. 绕过CDN查找网站真实IP方法收集

    方法1很简单,使用各种多地 ping 的服务,查看对应 IP 地址是否唯一,如果不唯一多半是使用了CDN, 多地 Ping 网站有: http://ping.chinaz.com/ http://pi ...

  6. <Lord don’t move that mountain>

    <Lord don’t move that mountain> Lord, don't move that mountain(主,不要移走高山) Give me strength to c ...

  7. Log4net 使用之 自定义字段

    Log4net 是.Net下一个非常优秀的开源日志记录组件.Log4net记录日志的功能非常强大.它可以将日志分不同的等级,以不同的格式,输出到不同的媒介. 由于业务需要,计划为日志增加2个字段,除了 ...

  8. 使用 libffi 实现 AOP

    核心还是利用oc消息的查找派发机制,进行类结构的动态修改,用新函数替换老函数,然后再调用老函数. 前言     众所周知,使用runtime的提供的接口,我们可以设定原方法的IMP,或交换原方法和目标 ...

  9. 1552/3506. [CQOI2014]排序机械臂【平衡树-splay】

    Description Input 输入共两行,第一行为一个整数N,N表示物品的个数,1<=N<=100000. 第二行为N个用空格隔开的正整数,表示N个物品最初排列的编号. Output ...

  10. 【[SDOI2016]生成魔咒】

    这是一道\(SA\)的练手好题 建议做之前先去做一下2408 之后你就肯定会做这道题了 首先上面那道题的答案就是 \[\sum_{i=1}^nn+1-sa[i]-het[i]\] 就是对于每一个后缀求 ...