[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;
}

转载于:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/8509106.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值