题面

题解

设 \(f[i][j]\) 代表长度为 \(i\) 的序列, 乘积模 \(m\) 为 \(j\) 的序列有多少个

转移方程如下

\[f[i + j][C] = \sum_{A*B\equiv C \pmod{m} }f[i][B] * f[j][A]
\]

复杂度是 \(O(nm^2)\) 的

考虑倍增, 用类似快速幂那样的东西

\[f[2 * i][C] = \sum_{A*B\equiv C \pmod{m} }f[i][B] * f[i][A]
\]

恩, 复杂度变为了 \(O(m^2logn)\) 的

继续优化

上式相当于一个东西, 看到这个地方

\[c[z] = \sum_{x*y \equiv z \pmod m}a[x]b[y]
\]

如果是这样一种形式

\[c[z] = \sum_{x+y=z}a[x]b[y]
\]

我们就可以用 NTT 优化了

我们知道对数可以把乘法转成加法

但是对数是一个实数, 我们需要考虑一个模意义下的对数

把原根当做底数就可以了, 于是我们将上式转化为

\[c[log_gz] = \sum_{log_gx+log_gy\equiv log_gz\pmod m}a[log_gx]b[log_gy]
\]

考虑到 \(log_gx+log_gy\) 可能会大于 \(m\)

但是它一定不会大于 \(2m\) , 所以我们对于 \(c[z]\) 这个位置, 加上 \(c[z + m - 1]\) , 再将 \(c[z + m - 1]\) 清零即可

Code

  1. #include <algorithm>
  2. #include <iostream>
  3. #include <cstring>
  4. #include <cstdio>
  5. #include <map>
  6. const int N = 40005;
  7. const int mod = 1004535809;
  8. using namespace std;
  9. int n, m, X, S, lim, cnt, r[N], g, gg, a[N], b[N], res[N], f[N], top, fact[20005];
  10. map<int, int> mp;
  11. template < typename T >
  12. inline T read()
  13. {
  14. T x = 0, w = 1; char c = getchar();
  15. while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
  16. while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
  17. return x * w;
  18. }
  19. int fpow(int x, int y, int p)
  20. {
  21. int res = 1;
  22. for( ; y; y >>= 1, x = 1ll * x * x % p)
  23. if(y & 1) res = 1ll * res * x % p;
  24. return res;
  25. }
  26. int getroot(int x)
  27. {
  28. top = 0;
  29. int rem = x - 1, p = rem;
  30. for(int i = 2; i * i <= x; i++)
  31. if(!(rem % i))
  32. {
  33. fact[++top] = i;
  34. while(!(rem % i)) rem /= i;
  35. }
  36. if(rem > 1) fact[++top] = rem;
  37. for(int flag = 1, i = 2; i <= p; i++, flag = 1)
  38. {
  39. for(int j = 1; j <= top && flag; j++)
  40. if(fpow(i, p / fact[j], x) == 1) flag = 0;
  41. if(flag) return i;
  42. }
  43. return -1;
  44. }
  45. void ntt(int *p, int opt)
  46. {
  47. for(int i = 0; i < lim; i++) if(i < r[i]) swap(p[i], p[r[i]]);
  48. for(int i = 1; i < lim; i <<= 1)
  49. {
  50. int rt = fpow(opt == 1 ? g : gg, (mod - 1) / (i << 1), mod);
  51. for(int j = 0; j < lim; j += (i << 1))
  52. {
  53. int w = 1;
  54. for(int k = j; k < j + i; k++, w = 1ll * w * rt % mod)
  55. {
  56. int x = p[k], y = 1ll * w * p[k + i] % mod;
  57. p[k] = (1ll * x + y) % mod, p[k + i] = (1ll * x - y + mod) % mod;
  58. }
  59. }
  60. }
  61. if(opt == -1)
  62. {
  63. int inv = fpow(lim, mod - 2, mod);
  64. for(int i = 0; i < lim; i++) a[i] = 1ll * a[i] * inv % mod;
  65. }
  66. }
  67. void mul(int *A, int *B, int *C)
  68. {
  69. for(int i = 0; i < lim; i++) a[i] = A[i], b[i] = B[i];
  70. ntt(a, 1), ntt(b, 1);
  71. for(int i = 0; i < lim; i++) a[i] = 1ll * a[i] * b[i] % mod;
  72. ntt(a, -1);
  73. for(int i = 0; i < m - 1; i++) a[i] = (1ll * a[i] + a[i + m - 1]) % mod, a[i + m - 1] = 0;
  74. for(int i = 0; i < lim; i++) C[i] = a[i];
  75. }
  76. int main()
  77. {
  78. n = read <int> (), m = read <int> (), X = read <int> (), S = read <int> ();
  79. g = getroot(m), gg = fpow(g, m - 2, m);
  80. for(int tmp = 1, i = 0; i < m - 1; i++, tmp = 1ll * tmp * g % m) mp[tmp] = i;
  81. for(int x, i = 1; i <= S; i++)
  82. {
  83. x = read <int> ();
  84. if(x) f[mp[x]]++;
  85. }
  86. res[mp[1]] = 1;
  87. for(lim = 1; lim <= 2 * m; lim <<= 1, cnt++); cnt--;
  88. for(int i = 0; i < lim; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) << cnt);
  89. g = getroot(mod), gg = fpow(g, mod - 2, mod);
  90. while(n)
  91. {
  92. if(n & 1) mul(res, f, res);
  93. mul(f, f, f);
  94. n >>= 1;
  95. }
  96. printf("%d\n", res[mp[X]]);
  97. return 0;
  98. }

[题解] [SDOI2015] 序列统计的更多相关文章

  1. 【题解】SDOI2015序列统计

    [题解]SDOI2015序列统计 来自永不AFO的YYB的推荐 这里是乘积,比较麻烦,不过由于给定的序列膜数是个小质数,所以可以\(O(m^2\log m)\)找原跟(实际上不需要这么多). 乘积有点 ...

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

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

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

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

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

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

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

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

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

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

  7. [SDOI2015]序列统计

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

  8. 3992: [SDOI2015]序列统计

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

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

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

随机推荐

  1. CentOS下 .Net Core 1.0 升级到 3.0 遇到的一个小问题

    之前.net core 1.0的安装方式,不是用yum方式安装的,所以,在用yum安装3.0之后,用dotnet --version还是1.0的版本,想起了之前 做过链接映射dotnet目录,删除之前 ...

  2. Java框架 高并发系列 1第1天:必须知道的几个概念

    https://mp.weixin.qq.com/s?__biz=MzA5MTkxMDQ4MQ==&mid=2648933019&idx=1&sn=3455877c451de9 ...

  3. c#基础知识梳理(三)

    上期回顾 - https://www.cnblogs.com/liu-jinxin/p/10824638.html 一.方法 一个方法是把一些相关的语句组织在一起,用来执行一个任务的语句块.每一个 C ...

  4. ndk-build 修改输出so位置 (change ndk-build output so lib file path )

    期望的目录结构: Folder --- | --- build.bat | --- Source | --- All sources codes *.cpp *.h | --- Android --- ...

  5. MRC下delegate 野指针问题

    最近项目开发中,临时被调去修复一个页面返回时crash的问题.出现这个问题的原因也很巧合,正好服务地址在同事电脑上,也正巧网络请求响应时间狂慢!一个请求发出去回来的时间是40秒左右,要是在线上,肯定会 ...

  6. 【已解决】项目加载失败,Web应用程序项目XX已配置为使用IIS

    这个解决方法是我在网上参考了很多方法都不行,因为昨天还好好的,今天就不行,那跟项目没多大关系,跟环境有关. 解决方案: 本地iis和vs自带的iis冲突了,默认用了本地的iis,我删掉本地的就可以了. ...

  7. vim调试Shell脚本: unexpected EOF while looking for matching

    往往在编写脚本完后测试,出现错误需要调试,vim 是一种强大的文本编辑器,对调试也很有帮助.如果指定用不同的颜色显示某些错误,通过配置 .vimrc 文件就会替您完成大部分调试工作. 小柏在测试脚本时 ...

  8. 用javafx webview 打造自己的浏览器

    背景 项目需要做一个客户端的壳,内置浏览器,访问指定 的url 采用技术 java 1.8 开始吧! java环境配置略 hello world import javafx.application.A ...

  9. Tomcat 启动闪退解决

    直接使用cmd进入tomcat 目录下进行启动,直接使用Catalina.bat run运行,查看报啥错.进一步进行解决

  10. Ubuntu系统---C++之VScode IDE 编译器安装

    Ubuntu系统---C++之VScode IDE 编译器安装 简单了解了一下VScode,直观印象:安装包很小(不像VS那么大占用十G左右).跨平台.小巧.可以编译C++ / java / pyth ...