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