BZOJ 1409: Password 线性筛+矩阵乘法

Description

Rivest是密码学专家。近日他正在研究一种数列E = {E[1],E[2],……,E[n]},
且E[1] = E[2] = p(p为一个质数),E[i] = E[i-2]*E[i-1] (若小i<=n)。

例如{2,2,4,8,32,256,8192,……}就是p = 2的数列。在此基础上他又设计了一种加密算法,该算法可以通过一个密钥q (q < p)将一个正整数n加密成另外一个正整数d,计算公式为:d = E[n] mod q。现在Rivest想对一组数据进行加密,但他对程序设计不太感兴趣,请你帮助他设计一个数据加密程序。
Input

第一行读入m,p。其中m表示数据个数,p用来生成数列E。 以下有m行,每行有2个整数n,q。n为待加密数据,q为密钥。 数据范围: 0 < p n< 2^31 0 < q < p 0 < m <= 5000。
Output

将加密后的数据按顺序输出到文件 第i行输出第i个加密后的数据。 输入样例1 2 7 4 5 4 6 输入样例2 4 7 2 4 7 1 6 5 9 3
Sample Input
输入样例1

2 7

4 5

4 6

输入样例2

4 7

2 4

7 1

6 5

9 3
Sample Output
输出样例1

3

1

输出样例2

3

0

1

1

解法:

E[i]=p^f[i]

f[i]表示斐波那契数列的第i项

E[i]%q

=p^f[i]%q

因为q

///BZOJ 1409 线形筛

#include <bits/stdc++.h>
using namespace std;
///求解p^f(n)%q,其中f(n)为fib的第n项
typedef long long LL;
const int maxn = 100010;
LL phi;
struct mat{
    LL a[2][2];
    void init(){
        memset(a, 0, sizeof(a));
    }
};
mat operator * (mat a, mat b){
    mat res;
    res.init();
    for(int i=0; i<2; i++)
        for(int j=0; j<2; j++)
            for(int k=0; k<2; k++)
                res.a[i][j]=(res.a[i][j]+a.a[i][k]*b.a[k][j]%phi)%phi;
    return res;
}
int powmod(int x, int y, int mod)
{
    int ans=1LL;
    while(y){
        if(y&1) ans=(long long)ans*x%mod;
        x=(long long)x*x%mod;
        y>>=1;
    }
    return ans%mod;
}
int pri[maxn];
bool mark[maxn];
int tot, T, p, n, q;
void xianxingsai(){
    memset(mark, 0, sizeof(mark));
    for(int i=2; i<maxn; i++){
        if(!mark[i]) pri[++tot]=i;
        for(int j=1; j<=tot&&i*pri[j]<maxn; j++){
            mark[i*pri[j]]=1;
            if(i%pri[j]==0){
                break;
            }
        }
    }
}
int f1(int x)
{
    int ans=x;
    for(int i=1; i<=tot&&(long long)pri[i]*pri[i]<=x; i++)
    if(x%pri[i]==0){
        while(x%pri[i]==0) x/=pri[i];
        ans=ans/pri[i]*(pri[i]-1);
    }
    if(x!=1) ans=ans/x*(x-1);
    return ans;
}
int f2(int x){
    x--;
    mat a, b;
    a.a[0][0]=a.a[0][1]=a.a[1][0]=1;
    a.a[1][1]=0;
    b.a[0][0]=1; b.a[1][0]=0;
    while(x){
        if(x&1) b=a*b;
        a=a*a;
        x>>=1;
    }
    return b.a[0][0];
}
int main()
{
    scanf("%d%d", &T,&p);
    xianxingsai();
    while(T--){
        scanf("%d%d", &n,&q);
        phi=f1(q);
        int ans = powmod(p,f2(n),q);
        printf("%d\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值