[BZOJ1409]Password
试题描述
Rivest 是密码学专家。近日他正在研究一种数列 \(E = \{E[1],E[2], \cdots ,E[n]\}\),且 \(E[1] = E[2] = p\)(\(p\) 为一个质数),\(E[i] = E[i-2] \times E[i-1]\)(若 \(2 < i \le n\))。
例如 \(\{2,2,4,8,32,256,8192,\cdots\}\) 就是 \(p = 2\) 的数列。在此基础上他又设计了一种加密算法,该算法可以通过一个密钥 \(q (q < p)\) 将一个正整数 \(n\) 加密成另外一个正整数 \(d\),计算公式为:\(d = E[n] \mod q\)。现在 Rivest 想对一组数据进行加密,但他对程序设计不太感兴趣,请你帮助他设计一个数据加密程序。
输入
第一行读入 \(m\),\(p\) 。其中 \(m\) 表示数据个数,\(p\) 用来生成数列 \(E\)。 以下有 \(m\) 行,每行有 \(2\) 个整数 \(n\),\(q\)。\(n\) 为待加密数据,\(q\) 为密钥。
输出
将加密后的数据按顺序输出到文件 第 \(i\) 行输出第 \(i\) 个加密后的数据。
输入示例1
2 7
4 5
4 6
输出示例1
3
1
输入示例2
4 7
2 4
7 1
6 5
9 3
输出示例2
3
0
1
1
数据规模及约定
\(0 < p, n< 2^{31}\),\(0 < q < p\),\(0 < m \le 5000\)。
题解
欧拉定理:对于 \(\gcd(n, a) = 1\) 的数对 \(n, a\),满足 \(a^{\varphi(n)} \equiv 1 (\mod n)\)。(其中 \(\varphi(n)\) 表示欧拉函数,即 \(1 \sim n\) 中与 \(n\) 互质的数的个数)
知道这个定理,做法就显然了。
由于 \(q < p\),且 \(p\) 是质数,那么 \(\gcd(p, q) = 1\),从而 \(p^{\varphi(q)} \equiv 1 (\mod q)\),这样,\(p\) 的整数次幂就是以 \(\varphi(q)\) 为长度循环的了,所以对于指数可以直接对 \(\varphi(q)\) 取模。
指数是什么呢?正好就是斐波那契数列错了一位,矩阵快速幂即可。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
#define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
int read() {
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
#define maxn 47010
#define LL long long
bool vis[maxn];
int prime[maxn], cp;
void init() {
int n = maxn - 1;
rep(i, 2, n) {
if(!vis[i]) prime[++cp] = i;
for(int j = 1; j <= cp && i * prime[j] <= n; j++) {
vis[i*prime[j]] = 1;
if(i % prime[j] == 0) break;
}
}
return ;
}
int MOD;
struct Matrix {
int A[2][2];
Matrix() {}
Matrix operator * (const Matrix& t) const {
Matrix ans;
ans.A[0][0] = ((LL)A[0][0] * t.A[0][0] + (LL)A[0][1] * t.A[1][0]) % MOD;
ans.A[0][1] = ((LL)A[0][0] * t.A[0][1] + (LL)A[0][1] * t.A[1][1]) % MOD;
ans.A[1][0] = ((LL)A[1][0] * t.A[0][0] + (LL)A[1][1] * t.A[1][0]) % MOD;
ans.A[1][1] = ((LL)A[1][0] * t.A[0][1] + (LL)A[1][1] * t.A[1][1]) % MOD;
return ans;
}
Matrix operator *= (const Matrix& t) { *this = *this * t; return *this; }
} base, tr;
Matrix PowM(Matrix a, int b) {
Matrix ans = a, t = a; b--;
while(b) {
if(b & 1) ans *= t;
t *= t; b >>= 1;
}
return ans;
}
int Pow(int a, int b, int m) {
int ans = 1, t = a;
while(b) {
if(b & 1) ans = (LL)ans * t % m;
t = (LL)t * t % m; b >>= 1;
}
return ans;
}
int main() {
init();
int T = read(), p = read();
tr.A[0][0] = 0;
tr.A[0][1] = tr.A[1][0] = tr.A[1][1] = 1;
while(T--) {
int n = read(), q = read(), phi = q, tmp = q;
if(n <= 2){ printf("%d\n", p % q); continue; }
rep(i, 1, cp) {
if(tmp % prime[i] == 0) {
phi = phi / prime[i] * (prime[i] - 1);
while(tmp % prime[i] == 0) tmp /= prime[i];
}
if(tmp == 1) break;
}
if(tmp > 1) phi = phi / tmp * (tmp - 1);
MOD = phi;
base.A[0][0] = base.A[1][0] = 1;
base.A[0][1] = base.A[1][1] = 0;
base = PowM(tr, n - 1) * base;
printf("%d\n", Pow(p % q, base.A[0][0], q) % q);
}
return 0;
}