codevs1928 加密算法 数论[四星]

题目链接:CodeVs
妈呀长知识了……

题目描述 Description

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

输入描述 Input Description

读入m,p。其中m表示数据个数,p用来生成数列E。以下有m行,每行有2个整数n,q。n为待加密数据,q为密钥。
规模:0

输出描述 Output Description

将加密后的数据按顺序输出到文件a.out。
第i行输出第i个加密后的数据。

样例输入 Sample Input

2 7
4 5
4 6

样例输出 Sample Output

3
1

数据范围及提示 Data Size & Hint

题解:

首先看题目,暴力枚举模拟基本没分……20个点说不定也可以水一点分,所以我们不考虑暴力。

对于题目中隐含的公式:E[i] = E[i-1]*E[i-2];
虽然看起来和fib没有什么关系,也不能直接推矩阵公式,但是我们拆开后发现每一项都是题目给出p的fib级。
这么说难以理解,直接模拟一下:

E[1] = p;
E[2] = p;
E[3] = E[2] * E[1] = p ^ 2 = p ^ (fib(3));
E[4] = E[3] * E[2] = p ^ 3 = p ^ (fib(4));
E[5] = E[4] * E[3] = p ^ 5 = p ^ (fib(5));
……
……
……

那么题目给定的原公式就可以化简一下:

d=E[n] mod q ——–> d = p^fib(n) % q;
经过fib转化后就可以用钜乘了,但是还有一个问题,这里乱取膜真的没问题吗?

我的第一代想法就是 p ^ fib(n) % q == (p%q) ^ (fib(n) % q) % q,但是被神犇Archon告知这样是完全错误的……
于是她安利了我一个公式 :
迷の公式
这是正确姿势,于是我就比这个打,然后以前只会筛,现在也学会了根号n求欧拉函数,感觉收获挺大的~
钜乘什么的以前写过多次了,这里就是要注意多组数据每次都清掉以前的结果就行了~
下面写一下如何根号n求phi(n);

定理:1、若 p 是质数,φ(p)= p-1.
2、若 n 是质数 p 的 k 次幂,φ(n)= (p-1)p^(k-1)
因为除了 p 的倍数都与 n 互质

因为n % p == 0只可能存在于[1,sqrt(n)];所以算法复杂度是sqrt(n)~
下面写一下:

int get(int n)
{
    int ret = 1;
    for(int i = 2;i*i <= n;i ++)
    {
        if(n % i == 0)
        {
            n /= i,ret *= (i-1);//定理2
            while(n % i == 0)   n /= i,ret *= i;//定理2
        }
    }
    if(n > 1)   ret *= (n-1);
    return ret;
}

下面附上完整代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<stack>
#include<algorithm>
using namespace std;
typedef unsigned long long LL;
LL mod,phimod;
struct juzhen{
    LL num[3][3];
    LL n,m;
    juzhen(){memset(num,0,sizeof(num)); n= 0,m = 0;};
};

juzhen operator * (juzhen a,juzhen b)
{
    juzhen ans;
    ans.n = a.n,ans.m = b.m;
    for(int i = 1;i <= a.n;i ++)
    {
        for(int j = 1;j <= b.m;j ++)
        {
            ans.num[i][j] = 0;
            for(int k = 1;k <= a.m;k ++)
            {
                ans.num[i][j] = (ans.num[i][j]%phimod + ((a.num[i][k]%phimod)*(b.num[k][j]%phimod))%phimod)%phimod;
            }
//          cout<<ans.num[i][j]<<"QAQ"<<endl;
        }
    }
    return ans;
}
juzhen fib,tmp;

int get(int n)
{
    int ret = 1;
    for(int i = 2;i*i <= n;i ++)
    {
        if(n % i == 0)
        {
            n /= i,ret *= (i-1);
            while(n % i == 0)   n /= i,ret *= i;
        }
    }
    if(n > 1)   ret *= (n-1);
    return ret;
}

void ksm(LL k)
{
    while(k)
    {
        if(k & 1)   fib = fib * tmp;
        tmp = tmp * tmp;
        k >>= 1;
    }
}
LL kusm(LL a,LL b)
{
    LL ans = 1;
    while(b)
    {
        if(b & 1)   ans = ((ans%mod)*(a%mod))%mod;
        a = ((a%mod)*(a%mod))%mod;
        b >>= 1;
    }
    return ans;
}

LL m,p,n;
int main()
{
    ios::sync_with_stdio(0);
    cin>>m>>p;
    while(m--)
    {
        fib.n = 1,fib.m = 2;
        fib.num[1][1] = fib.num[1][2] = 1;
        tmp.n = tmp.m = 2;
        tmp.num[1][1] = tmp.num[1][2] = tmp.num[2][1] = 1;
        tmp.num[2][2] = 0;
        cin>>n>>mod;
        phimod = get(mod);
//      cout<<mod<<"QAQ"<<endl;
        if(n >= 3LL)    ksm(n-2);
        LL cifang = fib.num[1][1] + phimod;
        LL ans = kusm(p,cifang);
        cout<<ans<<endl;
    }
    return 0;
}

NOIP ;Bless All ;RP++;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值