题解 P4454 【[CQOI2018]破解D-H协议】

题目链接:Link

Problem

题目背景

Diffie-Hellman密钥交换协议是一种简单有效的密钥交换方法。它可以让通讯双方在没有事先约定密钥(密码) 的情况下,通过不安全的信道(可能被窃听) 建立一个安全的密钥K,用于加密之后的通讯内容。

题目描述

假定通讯双方名为Alice和Bob,协议的工作过程描述如下(其中 mod 表示取模运算) :

  1. 协议规定一个固定的质数P,以及模P 的一个原根g。P 和g 的数值都是公开的,无需保密。
  2. Alice 生成一个随机数a,并计算 $ A=g^a \bmod P $ , 将A 通过不安全信道发送给Bob。
  3. Bob 生成一个随机数b,并计算 $ B=g^b \bmod P $ ,将B 通过不安全信道发送给Alice。
  4. Bob 根据收到的A 计算出 $ K=A^b \bmod P $ ,而Alice 根据收到的B 计算出 $ K=B^a \bmod P $ 。
  5. 双方得到了相同的K,即 $ g^{ab} \bmod 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)} \bmod 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\n",a,b);
		printf("%lld\n",ksm(B,BSGS(g,A))%p);
	}
	return 0;
}
posted @ 2019-08-19 20:50  happyZYM  阅读(123)  评论(0编辑  收藏  举报