codevs1928 加密算法 数论[四星]

来源:互联网 发布:十二宫杀手解析知乎 编辑:程序博客网 时间:2024/05/22 01:51

题目链接: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