[BZOJ1409]Password

[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;
}
posted @ 2018-03-05 15:13  xjr01  阅读(179)  评论(0编辑  收藏  举报