【BZOJ3328】PYXFIB(数学)
什么都不会的数学蒻菜瑟瑟发抖……Orz橙子(和兔子)
题目:
分析:
橙子给我安利的数学题……(然后我就看着他因为矩阵乘法多模了一次卡了一天常数qwq表示同情)
先考虑一个子问题:求下面这个式子的值:
\]
首先我们知道\(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\)具有如下性质:
\]
证明?当\(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\)的倍数的情况的目的。那么要求的那个式子就变成了:
\]
发现后面那一堆和\(i\)没什么关系,提到前面来(相当于把\(w_k^{ij}\)分配进去):
\]
好像挺像二项式定理的,只是\(w_k^j\)的指数比较丑。但我们可以这样……
\]
然后设\(x_j=w_k^{-j}\),得到:
\]
于是可以上矩阵二项式定理了(注意因为\(x_j\)是一个整数,所以要乘上单位矩阵\(I\)才能与\(A\)相加)。
\]
照着这个就可以直接算了qwq
代码:
矩阵乘法的时候一定不要模两次!一定不要模两次!一定不要模两次!否则你会像橙子一样卡一天常数。
以及记着用上面提到过的欧拉定理把所有指数搞成非负的。
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#define _ 0
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;
ll n;
int k, p;
struct Matrix
{
int n, m, data[2][2];
Matrix(const int _n = 0, const int _m = 0)
: n(_n), m(_m)
{
for (int i = 0; i < n; i++)
memset(data[i], 0, sizeof(int[m]));
}
~Matrix() {}
Matrix operator + (const Matrix &b) const
{
Matrix ans(n, m);
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
ans[i][j] = (data[i][j] + b[i][j]) % p;
return ans;
}
Matrix operator * (const Matrix &b) const
{
Matrix ans(n, b.m);
for (int i = 0; i < n; i++)
for (int k = 0; k < m; k++)
for (int j = 0; j < b.m; j++)
ans[i][j] = (ans[i][j] +
(ll)data[i][k] * b[k][j] % p) % p;
return ans;
}
Matrix operator * (const ll &x) const
{
Matrix ans(n, m);
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
ans[i][j] = ((ll)data[i][j] * x) % p;
return ans;
}
const int *operator [] (const int a) const
{
return data[a];
}
int *operator [] (const int a)
{
return data[a];
}
};
inline Matrix get_identity(const int n)
{
Matrix ans(n, n);
for (int i = 0; i < n; i++)
ans[i][i] = 1;
return ans;
}
inline Matrix power(Matrix a, ll b)
{
Matrix ans = get_identity(a.n);
while (b)
{
if (b & 1)
ans = ans * a;
a = a * a;
b >>= 1;
}
return ans;
}
inline int power(int a, ll b)
{
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)
{
return power(a, p - 2);
}
pair<int, int> prime[20];
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 p)
{
get_prime(p - 1);
for (int i = 2; i < p; i++)
{
bool flag = true;
for (int j = 0; j < cnt && flag; j++)
if (power(i, (p - 1) / prime[j].first) == 1)
flag = false;
if (flag)
return i;
}
}
int work()
{
int T;
read(T);
Matrix I = get_identity(2);
Matrix A(2, 2);
A[0][0] = A[0][1] = A[1][0] = 1;
while (T--)
{
read(n), read(k), read(p);
int omega = power(get_g(p), (p - 1) / k), ans = 0;
int tmp = power(omega, p - k - 1);
for (int i = 0; i > -k; i--)
{
tmp = (ll)tmp * omega % p;
ans = (ans + (ll)power(tmp, -n) * power(I * tmp + A, n)[0][0] % p) % p;
}
write((ll)ans * inv(k) % p);
putchar('\n');
}
return (0^_^0);
}
}
int main()
{
freopen("3328.in", "r", stdin);
freopen("3328.out", "w", stdout);
return zyt::work();
}
【BZOJ3328】PYXFIB(数学)的更多相关文章
- BZOJ3328 PYXFIB 单位根反演
题意:求 \[ \sum_{i=0}^n[k|i]\binom{n}{i}Fib(i) \] 斐波那契数列有简单的矩阵上的通项公式\(Fib(n)=A^n_{1,1}\).代入得 \[ =\sum_{ ...
- BZOJ3328: PYXFIB
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3328 题解:关键在于只处理i%k的项,那么我们就需要用一个式子来表达这个东西. p%k==1. ...
- bzoj3328: PYXFIB(单位根反演+矩阵快速幂)
题面 传送门 题解 我们设\(A=\begin{bmatrix}1 & 1 \\ 1 & 0\end{bmatrix}\),那么\(A^n\)的左上角就是\(F\)的第\(n\)项 所 ...
- 【Luogu5293】[HNOI2019] 白兔之舞
题目链接 题目描述 略 Sol 考场上暴力 \(O(L)\) 50分真良心. 简单的推一下式子,对于一个 t 来说,答案就是: \[\sum_{i=0}^{L} [k|(i-t)] {L\choose ...
- 【BZOJ3328】PYXFIB 数论+矩阵乘法
[BZOJ3328]PYXFIB Description Input 第一行一个正整数,表示数据组数据 ,接下来T行每行三个正整数N,K,P Output T行,每行输出一个整数,表示结果 Sampl ...
- 【BZOJ3328】PYXFIB(单位根反演,矩阵快速幂)
[BZOJ3328]PYXFIB(单位根反演,矩阵快速幂) 题面 BZOJ 题解 首先要求的式子是:\(\displaystyle \sum_{i=0}^n [k|i]{n\choose i}f_i\ ...
- 数学思想:为何我们把 x²读作x平方
要弄清楚这个问题,我们得先认识一个人.古希腊大数学家 欧多克索斯,其在整个古代仅次于阿基米德,是一位天文学家.医生.几何学家.立法家和地理学家. 为何我们把 x²读作x平方呢? 古希腊时代,越来越多的 ...
- 速算1/Sqrt(x)背后的数学原理
概述 平方根倒数速算法,是用于快速计算1/Sqrt(x)的值的一种算法,在这里x需取符合IEEE 754标准格式的32位正浮点数.让我们先来看这段代码: float Q_rsqrt( float nu ...
- MarkDown+LaTex 数学内容编辑样例收集
$\color{green}{MarkDown+LaTex 数学内容编辑样例收集}$ 1.大小标题的居中,大小,颜色 [例1] $\color{Blue}{一元二次方程根的分布}$ $\color{R ...
随机推荐
- Java 数组中寻找最大子数组
程序设计思想: 依次将数组划分开,先判断一个元素的单个数组大小,接下来两个,依次上升,最后将所得结果进行比较赋值,输出最大结果. 1 package ketangTest; //张生辉,康治家 201 ...
- Python进阶-操作redis
1.String 操作 redis中的String在在内存中按照一个name对应一个value来存储 set() #在Redis中设置值,默认不存在则创建,存在则修改 r.set('name', 'z ...
- Linux 复习四
第四章 shell程序设计I-入门 一.shell脚本的基本概念 shell脚本(script)是一个可执行的纯文本文件,有多个shell命令组成. 命令的执行时从上而下.从左而右的分析和执行 命令. ...
- 【Codeforces 1030D】Vasya and Triangle
[链接] 我是链接,点我呀:) [题意] 题意 [题解] 参考这篇题解:https://blog.csdn.net/mitsuha_/article/details/82825862 为什么可以保证m ...
- POJ 2356 && POJ 3370 鸽巢原理
POJ 2356: 题目大意: 给定n个数,希望在这n个数中找到一些数的和是n的倍数,输出任意一种数的序列,找不到则输出0 这里首先要确定这道题的解是必然存在的 利用一个 sum[i]保存前 i 个数 ...
- 产品需求分析神器:KANO模型分析法
前言: 任何一个互联网产品,哪怕是一个简单的页面,也会涉及到很多的需求,产品经理也会经常遇到这样的情况:老板,业务提的各种新需求一下子都扎堆,哪个需求对用户来说最重要,用户对我们的新功能是否满意?开发 ...
- nyoj_600_花儿朵朵_201404162000
花儿朵朵 时间限制:1000 ms | 内存限制:65535 KB 难度:5 描述 春天到了,花儿朵朵盛开,hrdv是一座大花园的主人,在他的花园里种着许多种鲜花,每当这个时候,就会有一大群游 ...
- Ubuntu 16.04解决在虚拟终端(Ctrl+Alt+F1)下显示菱形中文乱码问题
在安装Ubuntu时,如果默认选择了中文,那么以后进去到虚拟终端就会出现菱形的中文乱码. 其实这个是无解的,但是可以通过以下技巧去实现: 1.把系统转成英文的 sudo gedit /etc/defa ...
- CloudEngine 6800基础配置-02_常用命令操作
查看未提交配置 system-view ftp server enable display configuration candidate 删除未提交的配置 clear configurati ...
- LeetCode 387. First Unique Character in a String (字符串中的第一个唯一字符)
题目标签:String, HashMap 题目给了我们一个 string,让我们找出 第一个 唯一的 char. 设立一个 hashmap,把 char 当作 key,char 的index 当作va ...