题目:

洛谷3321

分析:

一个转化思路比较神(典型?)的题……

一个比较显然的\(O(n^3)\)暴力是用\(f[i][j]\)表示选了\(i\)个数,当前积在模\(m\)意义下为\(j\)的方案数,每次转移枚举\(S\)的元素,即(\(k^{-1}\)表示\(k\)在模\(m\)意义下的逆元):

\[f[i][j]=\sum_{k\in S} f[i-1][jk^{-1}]
\]

事实上写的时候通常是从\(f[i][j]\)往\(f[i+1][jk]\)贡献

然后通过Orz题解发现那个乘法\(jk^{-1}\)非常地丑,如果能变成加法就好了qwq。注意到保证\(m\)是一个质数,所以是可以求原根的。原根的好处在于模\(m\)的意义下\(g^i(i\in [0,m-1))\)与\(a(a \in [1,m-1])\)一一对应。所以如果用原根的幂代替原数,原数的乘法就变成了指数的加法。即设\(f[i][j]\)表示选了\(i\)个数,积在模\(m\)意义下是\(g^j\)的方案数,则:

\[f[i][j]=\sum_{g^k \in S} f[i-1][j-k]
\]

上面那个东西像不像卷积?一点都不像看到\(j-k\)难道你就不想再来一个跟\(k\)有关的东西吗?于是定义一个函数\(h(x)\):

\[h(x)=\begin{cases}1&(g^x\in S) \\
0&(otherwise)\end{cases}\]

那么就成了:

\[f[i][j]=\sum_{k=0}^{m-2}h(k)f[i-1][j-k]
\]

设一个多项式\(A\),第\(i\)项系数是\(h(i)\),则一开始\(f[1]\)就是\(A\),所以答案就是\(A^n\)的\(x\)次项系数,写一个多项式快速幂即可。

代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <set>
using namespace std; namespace zyt
{
template<typename T>
inline bool read(T &x)
{
char c;
bool f = false;
x = 0;
do
c = getchar();
while (c != EOF && c != '-' && !isdigit(c));
if (c == EOF)
return false;
if (c == '-')
f = true, c = getchar();
do
x = x * 10 + c - '0', c = getchar();
while (isdigit(c));
if (f)
x = -x;
return true;
}
template<typename T>
inline void write(T x)
{
static char buf[20];
char *pos = buf;
if (x < 0)
putchar('-'), x = -x;
do
*pos++ = x % 10 + '0';
while (x /= 10);
while (pos > buf)
putchar(*--pos);
}
typedef long long ll;
const int N = 8010, p = 1004535809;
int n, m;
set<int> s;
namespace Polynomial
{
const int LEN = N << 2;
int omega[LEN], winv[LEN], rev[LEN];
inline int power(int a, int b, const int p = ::zyt::p)
{
int ans = 1;
while (b)
{
if (b & 1)
ans = (ll)ans * a % p;
a = (ll)a * a % p;
b >>= 1;
}
return ans;
}
inline int inv(const int a, const int p = ::zyt::p)
{
return power(a, p - 2);
}
namespace Primitive_Root
{
pair<int, int>prime[N];
int cnt;
inline void get_prime(int n)
{
cnt = 0;
for (int i = 2; i * i <= n; i++)
{
if (n % i == 0)
{
prime[cnt++] = make_pair(i, 0);
while (n % i == 0)
++prime[cnt - 1].second, n /= i;
}
}
if (n > 1)
prime[cnt++] = make_pair(n, 1);
}
inline int get_g(const int n)
{
get_prime(n - 1);
for (int i = 2; i < n; i++)
{
bool flag = true;
for (int j = 0; j < cnt && flag; j++)
flag &= (power(i, (n - 1) / prime[j].first, n) != 1);
if (flag)
return i;
}
return -1;
}
}
void init(const int n, const int lg2)
{
static int g = 0;
if (!g)
g = Primitive_Root::get_g(p);
int w = power(g, (p - 1) / n), wi = inv(w);
omega[0] = winv[0] = 1;
for (int i = 1; i < n; i++)
{
omega[i] = (ll)omega[i - 1] * w % p;
winv[i] = (ll)winv[i - 1] * wi % p;
}
for (int i = 0; i < n; i++)
rev[i] = ((rev[i >> 1] >> 1) | ((i & 1) << (lg2 - 1)));
}
void ntt(int *a, const int *w, const int n)
{
for (int i = 0; i < n; i++)
if (i < rev[i])
swap(a[i], a[rev[i]]);
for (int l = 1; l < n; l <<= 1)
for (int i = 0; i < n; i += (l << 1))
for (int k = 0; k < l; k++)
{
int tmp = (a[i + k] - (ll)a[i + l + k] * w[n / (l << 1) * k] % p + p) % p;
a[i + k] = (a[i + k] + (ll)a[i + l + k] * w[n / (l << 1) * k] % p) % p;
a[i + l + k] = tmp;
}
}
void mul(const int *a, const int *b, int *c, const int n)
{
static int x[LEN], y[LEN];
memcpy(x, a, sizeof(int[n]));
memcpy(y, b, sizeof(int[n]));
int m = 1, lg2 = 0;
while (m < (n << 1))
m <<= 1, ++lg2;
init(m, lg2);
memset(x + n, 0, sizeof(int[m - n]));
memset(y + n, 0, sizeof(int[m - n]));
ntt(x, omega, m), ntt(y, omega, m);
for (int i = 0; i < m; i++)
x[i] = (ll)x[i] * y[i] % p;
ntt(x, winv, m);
int invm = inv(m);
for (int i = 0; i < n; i++)
c[i] = (ll)(x[i] + x[i + n]) * invm % p;
}
void power(const int *a, int b, int *c, const int n)
{
static int x[N];
memcpy(x, a, sizeof(int[n]));
memset(c, 0, sizeof(int[n]));
c[0] = 1;
while (b)
{
if (b & 1)
mul(c, x, c, n);
mul(x, x, x, n);
b >>= 1;
}
}
}
int A[N];
int work()
{
using Polynomial::power;
using Polynomial::Primitive_Root::get_g;
int x, ssize;
read(n), read(m), read(x), read(ssize);
for (int i = 0; i < ssize; i++)
{
int a;
read(a);
s.insert(a);
}
int g = get_g(m), gx = -1;
for (int i = 0; i < m - 1; i++)
{
int tmp = power(g, i, m);
if (s.count(tmp))
A[i] = 1;
if (tmp == x)
gx = i;
}
power(A, n, A, m - 1);
write(A[gx]);
return 0;
}
}
int main()
{
return zyt::work();
}

【洛谷3321_BZOJ3992】[SDOI2015]序列统计(原根_多项式)的更多相关文章

  1. [洛谷P3321][SDOI2015]序列统计

    题目大意:给你一个集合$n,m,x,S(S_i\in(0,m],m\leqslant 8000,m\in \rm{prime},n\leqslant10^9)$,求一个长度为$n$的序列$Q$,满足$ ...

  2. 洛谷P3321 [SDOI2015]序列统计(NTT)

    传送门 题意:$a_i\in S$,求$\prod_{i=1}^na_i\equiv x\pmod{m}$的方案数 这题目太珂怕了……数学渣渣有点害怕……kelin大佬TQL 设$f[i][j]$表示 ...

  3. 洛谷3321 SDOI2015 序列统计

    懒得放传送[大雾 有趣的一道题 前几天刚好听到Creed_神犇讲到相乘转原根变成卷积的形式 看到这道题当然就会做了啊w 对于m很小 我们暴力找原根 如果你不会找原根的话 出门左转百度qwq 找到原根以 ...

  4. 【bzoj3992】[SDOI2015]序列统计 原根+NTT

    题目描述 求长度为 $n$ 的序列,每个数都是 $|S|$ 中的某一个,所有数的乘积模 $m$ 等于 $x$ 的序列数目模1004535809的值. 输入 一行,四个整数,N.M.x.|S|,其中|S ...

  5. 洛咕 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)\)的 像我这 ...

  6. BZOJ3992 [SDOI2015]序列统计 【生成函数 + 多项式快速幂】

    题目 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数 列,数列中的每个数都属于集合S.小C用这个生成器生成了许多这样的数列.但是小C有一个问题 ...

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

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

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

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

  9. 【LG3321】[SDOI2015]序列统计

    [LG3321][SDOI2015]序列统计 题面 洛谷 题解 前置芝士:原根 我们先看一下对于一个数\(p\),它的原根\(g\)有什么性质(好像就是定义): \(g^0\%p,g^1\%p,g^2 ...

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

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

随机推荐

  1. vs2012+ winform+.net4.0发布如何在xp上运行

    今天在英文版vs2013打包发布4.0(非4.0 client)的winform时,遇到了在xp上无法运行的情况,.net framework 4.0在xp上已安装.在打包前,winform工程,即菜 ...

  2. Docker学习总结(17)——学会使用Dockerfile

    Docker.Dockerfile.Docker镜像.容器这些都是什么鸟? 老生常谈,再再再--普及一下: Docker: 最早是dotCloud公司出品的一套容器管理工具,但后来Docker慢慢火起 ...

  3. CF671C. Ultimate Weirdness of an Array

    n<=200000个<=200000的数问所有的f(i,j)的和,表示去掉区间i到j后的剩余的数字中任选两个数的最大gcd. 数论日常不会.. 先试着计算一个数组:Hi表示f(l,r)&l ...

  4. ECMAScript 6 入门学习笔记(零)——开始

    所有es6笔记都是我自己提出来的一些点,没有很详细的例子什么的,这个链接就是我看的教程,有需要的可以看看.(http://es6.ruanyifeng.com/#docs/intro) 1.ECMAS ...

  5. P1516 青蛙的约会 洛谷

    https://www.luogu.org/problem/show?pid=1516 题目描述 两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面.它们很高兴地发现它们住在同一条纬度线上, ...

  6. - > 动规讲解基础讲解五——最长公共子序列问题

    一些概念: (1)子序列: 一个序列A = a1,a2,……an,中任意删除若干项,剩余的序列叫做A的一个子序列.也可以认为是从序列A按原顺序保留任意若干项得到的序列. 例如:   对序列 1,3,5 ...

  7. 浅谈WEB标准

    WEB标准,WEB标准.可亲可爱的WEB,什么是你定下的标准呢.这几天又又一次回归最基础的知识了,OK.言归正传,什么是WEB标准.为什么要用WEB标准? 比方说,如今的浏览器版本号多吧,chrome ...

  8. 微軟将弃用 System.Data.OracleClient

    http://www.cnblogs.com/WizardWu/archive/2010/05/17/1737009.html 微軟将从 .NET 4 以后的版本弃用 System.Data.Orac ...

  9. ubuntu刪除升級后多余的内核

    列出所安装的内核 带有image的就是内核文件dpkg --get-selections|grep linuxlibselinux1 installlinux-386 installlinux-ima ...

  10. 设置默认訪问项目的client的浏览器版本号(IE版本号)

    在项目开发部署中.发现浏览器不兼容现象,在不处理兼容性情况下让用户更好体验(IE浏览器) 我们来设置client默认訪问项目的浏览器版本号 例如以下所看到的的是不同IE版本号下的效果截图比較: IE5 ...