[BZOJ1409]Password
试题描述
Rivest 是密码学专家。近日他正在研究一种数列 (E = {E[1],E[2], cdots ,E[n]}),且 (E[1] = E[2] = p)((p) 为一个质数),(E[i] = E[i-2] imes 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
", 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
", Pow(p % q, base.A[0][0], q) % q);
}
return 0;
}