什么都不会的数学蒻菜瑟瑟发抖……Orz橙子(和兔子)

题目:

BZOJ3328

分析:

橙子给我安利的数学题……(然后我就看着他因为矩阵乘法多模了一次卡了一天常数qwq表示同情)

先考虑一个子问题:求下面这个式子的值:

\[\sum_{i=0}^n C_n^i F_i
\]

首先我们知道\(F_i=\left[\begin{matrix}1 & 1\\1 & 0\end{matrix}\right]^i\)。设那个矩阵为\(A\),即\(F_i=A^i\)。(注意这题斐波那契数列下标从\(0\)开始,所以\(F_2=2\)。)

(不知道?你把\(\left[\begin{matrix}F_i & F_{i-1}\\F_{i-2} & 0\end{matrix}\right]\)乘一下\(A\)试试。一开始左下方的值并不影响计算结果。)

然后\(\sum\limits_{i=0}^n C_n^i A^i\)这个东西好像长得挺像二项式定理的。事实上,如果两个矩阵相乘可以交换(即\(AB=BA\)),则这两个矩阵之和的幂可以用二项式定理展开。因此,上面那个式子就是\((A+I)^n\),其中\(I\)是单位矩阵(任何矩阵与单位矩阵相乘都是可交换的)。

(于是我推到这里忘了原题是什么兴高采烈地去给橙子说我会了

那么问题来了,如何“筛”掉当\(i\)不是\(k\)的倍数的时候对答案的贡献呢?介绍一个神奇的东西:单位根

首先先介绍原根。若在模\(p\)(这里只讨论\(p\)是质数的情况)意义下,\(a(0\leq a<p)\)是一个满足对于任意\(i(1\leq i < p - 1)\)都满足\(a^i\neq 1\)的整数,则\(a\)是\(p\)的原根。(根据欧拉定理,\(a^i=a^{i \mod \phi(p)}\),所以不讨论指数不小于\(\phi(p)\)即\(p-1\)的情况。)原根满足对于任意小于\(\phi(p)\)的非负整数\(i\),\(a^i\)在模\(p\)意义下互不相同。以下的“原根”都特指最小原根,记为\(g\)。求法?从\(2\)开始暴力枚举\(a\),把\(i=p'\)(其中\(p'\)是\(p-1\)的质因数)挨个试一遍看有没有\(a^i\equiv 1\mod p\)的情况就完了。(并不知道为什么是对的并且跑得飞快)

模\(p\)的\(k\)次单位根\(w_k=g^{\frac{p-1}{k}}\),所以\(w_k^k \equiv 1\mod p\)。

在模\(p\)的意义下,\(p\)的\(k\)次单位根\(w_k\)具有如下性质:

\[\sum_{i=0}^{k-1}w_k^{ij}=\begin{cases}k\ (j|k)\\0\ (otherwise)\end{cases}
\]

证明?当\(j|k\),\(ij\)全是\(k\)的倍数,所以\(w_k^{ij}=1\),全部加起来就是\(k\)。

否则,大力等比数列求和公式\(w_k^0\cdot \frac{1-w_k^{jk}}{1-w_k^j}\)。上面\(w_k^{jk}=1\),所以和是\(0\)。这个有点像FFT中的单位根,只是一个在单位圆上,一个在模域上,都是循环的(注意模域中指数的循环节是\(\phi(p)=p-1\))。

基于这个性质,我们达到了筛掉\(i\)不是\(k\)的倍数的情况的目的。那么要求的那个式子就变成了:

\[\sum_{i=0}^n C_n^i A^i\cdot\frac{1}{k}\sum_{j=0}^{k-1}w_k^{ij}
\]

发现后面那一堆和\(i\)没什么关系,提到前面来(相当于把\(w_k^{ij}\)分配进去):

\[\frac{1}{k}\sum_{j=0}^{k-1}\sum_{i=0}^n C_n^i A^iw_k^{ij}
\]

好像挺像二项式定理的,只是\(w_k^j\)的指数比较丑。但我们可以这样……

\[\frac{1}{k}\sum_{j=0}^{k-1}\sum_{i=0}^n C_n^i A^iw_k^{(-i)\cdot(-j)}
\]

然后设\(x_j=w_k^{-j}\),得到:

\[\frac{1}{k}\sum_{j=0}^{k-1}x_j^{-n}\sum_{i=0}^n C_n^i A^ix_j^{n-i}
\]

于是可以上矩阵二项式定理了(注意因为\(x_j\)是一个整数,所以要乘上单位矩阵\(I\)才能与\(A\)相加)。

\[\frac{1}{k}\sum_{j=0}^{k-1}x_j^{-n}(x_jI+A)^n
\]

照着这个就可以直接算了qwq

代码:

矩阵乘法的时候一定不要模两次!一定不要模两次!一定不要模两次!否则你会像橙子一样卡一天常数。

以及记着用上面提到过的欧拉定理把所有指数搞成非负的。

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <cctype>
  4. #include <algorithm>
  5. #define _ 0
  6. using namespace std;
  7. namespace zyt
  8. {
  9. template<typename T>
  10. inline bool read(T &x)
  11. {
  12. char c;
  13. bool f = false;
  14. x = 0;
  15. do
  16. c = getchar();
  17. while (c != EOF && c != '-' && !isdigit(c));
  18. if (c == EOF)
  19. return false;
  20. if (c == '-')
  21. f = true, c = getchar();
  22. do
  23. x = x * 10 + c - '0', c = getchar();
  24. while(isdigit(c));
  25. if (f)
  26. x = -x;
  27. return true;
  28. }
  29. template<typename T>
  30. inline void write(T x)
  31. {
  32. static char buf[20];
  33. char *pos = buf;
  34. if (x < 0)
  35. putchar('-'), x = -x;
  36. do
  37. *pos++ = x % 10 + '0';
  38. while (x /= 10);
  39. while (pos > buf)
  40. putchar(*--pos);
  41. }
  42. typedef long long ll;
  43. ll n;
  44. int k, p;
  45. struct Matrix
  46. {
  47. int n, m, data[2][2];
  48. Matrix(const int _n = 0, const int _m = 0)
  49. : n(_n), m(_m)
  50. {
  51. for (int i = 0; i < n; i++)
  52. memset(data[i], 0, sizeof(int[m]));
  53. }
  54. ~Matrix() {}
  55. Matrix operator + (const Matrix &b) const
  56. {
  57. Matrix ans(n, m);
  58. for (int i = 0; i < n; i++)
  59. for (int j = 0; j < m; j++)
  60. ans[i][j] = (data[i][j] + b[i][j]) % p;
  61. return ans;
  62. }
  63. Matrix operator * (const Matrix &b) const
  64. {
  65. Matrix ans(n, b.m);
  66. for (int i = 0; i < n; i++)
  67. for (int k = 0; k < m; k++)
  68. for (int j = 0; j < b.m; j++)
  69. ans[i][j] = (ans[i][j] +
  70. (ll)data[i][k] * b[k][j] % p) % p;
  71. return ans;
  72. }
  73. Matrix operator * (const ll &x) const
  74. {
  75. Matrix ans(n, m);
  76. for (int i = 0; i < n; i++)
  77. for (int j = 0; j < m; j++)
  78. ans[i][j] = ((ll)data[i][j] * x) % p;
  79. return ans;
  80. }
  81. const int *operator [] (const int a) const
  82. {
  83. return data[a];
  84. }
  85. int *operator [] (const int a)
  86. {
  87. return data[a];
  88. }
  89. };
  90. inline Matrix get_identity(const int n)
  91. {
  92. Matrix ans(n, n);
  93. for (int i = 0; i < n; i++)
  94. ans[i][i] = 1;
  95. return ans;
  96. }
  97. inline Matrix power(Matrix a, ll b)
  98. {
  99. Matrix ans = get_identity(a.n);
  100. while (b)
  101. {
  102. if (b & 1)
  103. ans = ans * a;
  104. a = a * a;
  105. b >>= 1;
  106. }
  107. return ans;
  108. }
  109. inline int power(int a, ll b)
  110. {
  111. int ans = 1;
  112. while (b)
  113. {
  114. if (b & 1)
  115. ans = (ll)ans * a % p;
  116. a = (ll)a * a % p;
  117. b >>= 1;
  118. }
  119. return ans;
  120. }
  121. inline int inv(const int a)
  122. {
  123. return power(a, p - 2);
  124. }
  125. pair<int, int> prime[20];
  126. int cnt;
  127. inline void get_prime(int n)
  128. {
  129. cnt = 0;
  130. for (int i = 2; i * i <= n; i++)
  131. {
  132. if (n % i == 0)
  133. prime[cnt++] = make_pair(i, 0);
  134. while (n % i == 0)
  135. ++prime[cnt - 1].second, n /= i;
  136. }
  137. if (n != 1)
  138. prime[cnt++] = make_pair(n, 1);
  139. }
  140. inline int get_g(const int p)
  141. {
  142. get_prime(p - 1);
  143. for (int i = 2; i < p; i++)
  144. {
  145. bool flag = true;
  146. for (int j = 0; j < cnt && flag; j++)
  147. if (power(i, (p - 1) / prime[j].first) == 1)
  148. flag = false;
  149. if (flag)
  150. return i;
  151. }
  152. }
  153. int work()
  154. {
  155. int T;
  156. read(T);
  157. Matrix I = get_identity(2);
  158. Matrix A(2, 2);
  159. A[0][0] = A[0][1] = A[1][0] = 1;
  160. while (T--)
  161. {
  162. read(n), read(k), read(p);
  163. int omega = power(get_g(p), (p - 1) / k), ans = 0;
  164. int tmp = power(omega, p - k - 1);
  165. for (int i = 0; i > -k; i--)
  166. {
  167. tmp = (ll)tmp * omega % p;
  168. ans = (ans + (ll)power(tmp, -n) * power(I * tmp + A, n)[0][0] % p) % p;
  169. }
  170. write((ll)ans * inv(k) % p);
  171. putchar('\n');
  172. }
  173. return (0^_^0);
  174. }
  175. }
  176. int main()
  177. {
  178. freopen("3328.in", "r", stdin);
  179. freopen("3328.out", "w", stdout);
  180. return zyt::work();
  181. }

【BZOJ3328】PYXFIB(数学)的更多相关文章

  1. BZOJ3328 PYXFIB 单位根反演

    题意:求 \[ \sum_{i=0}^n[k|i]\binom{n}{i}Fib(i) \] 斐波那契数列有简单的矩阵上的通项公式\(Fib(n)=A^n_{1,1}\).代入得 \[ =\sum_{ ...

  2. BZOJ3328: PYXFIB

    题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3328 题解:关键在于只处理i%k的项,那么我们就需要用一个式子来表达这个东西. p%k==1. ...

  3. bzoj3328: PYXFIB(单位根反演+矩阵快速幂)

    题面 传送门 题解 我们设\(A=\begin{bmatrix}1 & 1 \\ 1 & 0\end{bmatrix}\),那么\(A^n\)的左上角就是\(F\)的第\(n\)项 所 ...

  4. 【Luogu5293】[HNOI2019] 白兔之舞

    题目链接 题目描述 略 Sol 考场上暴力 \(O(L)\) 50分真良心. 简单的推一下式子,对于一个 t 来说,答案就是: \[\sum_{i=0}^{L} [k|(i-t)] {L\choose ...

  5. 【BZOJ3328】PYXFIB 数论+矩阵乘法

    [BZOJ3328]PYXFIB Description Input 第一行一个正整数,表示数据组数据 ,接下来T行每行三个正整数N,K,P Output T行,每行输出一个整数,表示结果 Sampl ...

  6. 【BZOJ3328】PYXFIB(单位根反演,矩阵快速幂)

    [BZOJ3328]PYXFIB(单位根反演,矩阵快速幂) 题面 BZOJ 题解 首先要求的式子是:\(\displaystyle \sum_{i=0}^n [k|i]{n\choose i}f_i\ ...

  7. 数学思想:为何我们把 x²读作x平方

    要弄清楚这个问题,我们得先认识一个人.古希腊大数学家 欧多克索斯,其在整个古代仅次于阿基米德,是一位天文学家.医生.几何学家.立法家和地理学家. 为何我们把 x²读作x平方呢? 古希腊时代,越来越多的 ...

  8. 速算1/Sqrt(x)背后的数学原理

    概述 平方根倒数速算法,是用于快速计算1/Sqrt(x)的值的一种算法,在这里x需取符合IEEE 754标准格式的32位正浮点数.让我们先来看这段代码: float Q_rsqrt( float nu ...

  9. MarkDown+LaTex 数学内容编辑样例收集

    $\color{green}{MarkDown+LaTex 数学内容编辑样例收集}$ 1.大小标题的居中,大小,颜色 [例1] $\color{Blue}{一元二次方程根的分布}$ $\color{R ...

随机推荐

  1. python字符串方法replace()简介

    今天写replace方法的时候的代码如下: message = "I really like dogs" message.replace('dog','cat') print(me ...

  2. Android 找不到资源的问题

    偶尔会遇到R.layout.***或R.id.***找不到资源的问题,明明在文件夹中有啊,那为什么嘞? 结合我自己遇到的情况和网上的资料,总结出以下几点可能的原因: 导入了android.R.这个是最 ...

  3. 08 Python基础数据结构

    目录: 1) 列表 2) 元组 3) 字符串 4) bytes 5) bytearray 6) 字典 7) 集合 8) 冻集合 """1. 列表特性2. 创建3. 增加4 ...

  4. 充当别的mcu的外部存储器(51类)

    // 锁存地址 - STC12C5A60S2 reg [15:0]rAddr_51; //存放51单片机传过来的地址 读51地址寄存器 always @ (posedge MCLKout or neg ...

  5. Springboot druid监控配置

    @Configuration public class DataSourceConfig { @Bean public ServletRegistrationBean statViewServlet( ...

  6. [luoguP1026] 统计单词个数(DP)

    传送门 题解 #include <cstdio> #include <cstring> #define max(x, y) ((x) > (y) ? (x) : (y)) ...

  7. CF671C. Ultimate Weirdness of an Array

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

  8. CODEVS1022 覆盖 (二分图染色+匈牙利算法)

    先对整幅图进行二分图染色,再跑一遍匈牙利算法. /* CODEVS1022 */ #include <cstdio> #include <cstring> #include & ...

  9. 洛谷 P2853 [USACO06DEC]牛的野餐Cow Picnic

    P2853 [USACO06DEC]牛的野餐Cow Picnic 题目描述 The cows are having a picnic! Each of Farmer John's K (1 ≤ K ≤ ...

  10. LSN

    http://www.cnblogs.com/lyhabc/archive/2013/07/16/3194220.html