题意:
Lcomyn 是个很厉害的选手,除了喜欢写17kb+的代码题,偶尔还会写数学题.他找到了一个数列:
fn=⎧⎩⎨⎪⎪1,ab,abfcn−1fn−2,n=1n=2otherwise他给了你几个数:n,a,b,c,你需要告诉他 fn 模p后的数值。
输入描述
第一行一个数T,为测试数据组数.
每组数据一行,一行五个正整数,按顺序为n,a,b,c,p.
1≤T≤10,1≤n≤1018,1≤a,b,c≤109 ,p是质数且 p≤109+7.
输出描述
对每组数据输出一行一个数,输出 fn 对p取模后的数值.
思路:
设
gn
=
logafn
,
gn
满足
gn=b+c∗gn−1+gn−2
。
可以转化为矩阵相乘:
⎧⎩⎨⎪⎪gngn−1b⎫⎭⎬⎪⎪=⎧⎩⎨⎪⎪c10100101⎫⎭⎬⎪⎪∗⎧⎩⎨⎪⎪gn−1gn−2b⎫⎭⎬⎪⎪
由此得:
⎧⎩⎨⎪⎪gngn−1b⎫⎭⎬⎪⎪=⎧⎩⎨⎪⎪c10100101⎫⎭⎬⎪⎪n−2∗⎧⎩⎨⎪⎪b0b⎫⎭⎬⎪⎪
用矩阵快速幂和费马小定理求出
gn
快速幂求出
agn
。
- 费马小定理:
假如p是质数,且gcd(a,p)=1,那么 ap−1 ≡ 1(mod p)。
注意a%p = 0的情况。(bc时就错在这了)
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long LL;
LL p;
void mul(LL a[3][3], LL b[3][3])
{
LL c[3][3];
for(int i = 0; i < 3; i++)
for(int j = 0; j < 3; j++)
{
c[i][j] = 0;
for(int k = 0; k < 3; k++)
{
c[i][j] += a[i][k] * b[k][j];
c[i][j] %= p-1; //费马小定理 p是素数,则a^(p-1)%p = 1;
}
}
for(int i = 0; i < 3; i++)
for(int j = 0; j < 3; j++)
a[i][j] = c[i][j];
}
void matpow(LL a[3][3], LL n)
{
LL ans[3][3];
memset(ans, 0, sizeof(ans));
for(int i = 0; i < 3; i++)
ans[i][i] = 1;
while(n)
{
if(n&1)
mul(ans, a);
mul(a, a);
n >>= 1;
}
for(int i = 0; i < 3; i++)
for(int j = 0; j < 3; j++)
a[i][j] = ans[i][j];
}
LL fastpow(LL a, LL b)
{
LL k = a;
LL ans = 1;
while(b)
{
if(b&1)
{
ans *= k;
ans %= p;
}
k *= k;
k %= p;
b >>= 1;
}
return ans;
}
int main(int argc, char const *argv[])
{
int t;
scanf("%d", &t);
while(t--)
{
LL n, a, b, c;
cin >> n >> a >> b >> c >> p;
if(n == 1)
cout << "1" << endl;
else if(a % p == 0)
cout << "0" << endl; //!!!!
else
{
LL mat[3][3] = {c, 1, 1,
1, 0, 0,
0, 0, 1};
matpow(mat, n-2);
LL ans[3][3] = {b, 0, 0,
0, 0, 0,
b, 0, 0};
mul(mat, ans);
cout << fastpow(a, mat[0][0]) << endl;
}
}
return 0;
}