题目链接:Link
Problem
题目背景
Diffie-Hellman密钥交换协议是一种简单有效的密钥交换方法。它可以让通讯双方在没有事先约定密钥(密码) 的情况下,通过不安全的信道(可能被窃听) 建立一个安全的密钥K,用于加密之后的通讯内容。
题目描述
假定通讯双方名为Alice和Bob,协议的工作过程描述如下(其中 mod 表示取模运算) :
- 协议规定一个固定的质数P,以及模P 的一个原根g。P 和g 的数值都是公开的,无需保密。
- Alice 生成一个随机数a,并计算 $ A=g^a mod P $ , 将A 通过不安全信道发送给Bob。
- Bob 生成一个随机数b,并计算 $ B=g^b mod P $ ,将B 通过不安全信道发送给Alice。
- Bob 根据收到的A 计算出 $ K=A^b mod P $ ,而Alice 根据收到的B 计算出 $ K=B^a mod P $ 。
- 双方得到了相同的K,即 $ g^{ab} mod P $。K 可以用于之后通讯的加密密钥。
可见,这个过程中可能被窃听的只有A、B,而a、b、K 是保密的。并且根据A、B、P、g 这4个数,不能轻易计算出K,因此K 可以作为一个安全的密钥。
当然安全是相对的,该协议的安全性取决于数值的大小,通常a、b、P 都选取数百位以上的大整数以避免被破解。然而如果Alice 和Bob 编程时偷懒,为了避免实现大数运算,选择的数值都小于2312^{31}231,那么破解他们的密钥就比较容易了。
输入输出格式
输入格式:
输入文件第一行包含两个空格分开的正整数g 和P。
第二行为一个正整数n, 表示Alice 和Bob 共进行了n 次连接(即运行了n 次协议)。
接下来n 行,每行包含两个空格分开的正整数A 和B,表示某次连接中,被窃听的A、B 数值。
输出格式:
输出包含n行,每行1个正整数K,为每次连接你破解得到的密钥。
输入输出样例
in #1
3 31
3
27 16
21 3
9 26
out #1
4
21
25
说明
对于30%的数据,$ 2 leq A,B,P leq 1000 ( 对于100%的数据,) 2 leq A,B le P le 2^{31} , 2 leq g le 20 , 1 leq n leq 20 $
Solution
在仔细揣摩题意后,不难发现,实际上就是求 $ g^a equiv A pmod{p} $ 中的a,然后我们就可以愉快地套用BSGS的模板了。
但有些事情要特别注意:
- 这道题目中每次BSGS的A都为g,所以预处理要放在循环外面防止重复计算
- 不全局开
long long
容易写炸 - 只要算出A和B中的一个的BSGS就行了,答案是$ B^{BSGS(g,A)} mod p $
Code
#include<cstdio>
#include<cmath>
#include<map>
using namespace std;
typedef long long LL;
LL g,p,A,B,a,b,n,m,inv;
map<LL,int> mp;
inline LL ksm(LL a,LL b) { LL res=1; for(;b;b>>=1,a=a*a%p) b&1?res=res*a%p:0; return res; }
inline LL BSGS(LL A,LL B)
{
A%=p; B%=p;
for(int i=0;i<m;i++)
{
if(mp[B]) return (i*m+mp[B])%p;
B=B*inv%p;
}
return -1;
}
int main()
{
#ifdef local
freopen("pro.in","r",stdin);
#endif
scanf("%lld%lld%lld",&g,&p,&n);
m=ceil(sqrt(p));
LL x=1;
for(int i=1;i<=m;i++)
{
x=x*g%p;
mp[x]=i;
}
inv=ksm(g,p-m-1)%p;
while(n-->0)
{
scanf("%lld%lld",&A,&B);
//printf("a=%d b=%d
",a,b);
printf("%lld
",ksm(B,BSGS(g,A))%p);
}
return 0;
}