题目链接: 落谷:https://www.luogu.org/problemnew/show/P4454
BZOJ: https://www.lydsy.com/JudgeOnline/problem.php?id=5296
题目描述
Diffie-Hellman密钥交换协议是一种简单有效的密钥交换方法。它可以让通讯双方在没有事先约定密钥(密码)的情况下,通过不安全的信道(可能被窃听)建立一个安全的密钥K,用于加密之后的通讯内容。
假定通讯双方名为Alice和Bob,协议的工作过程描述如下(其中mod表示取模运算):
协议规定一个固定的质数P,以及模P的一个原根g。P和g的数值都是公开的,无需保密。
Alice生成一个随机数a,并计算A=ga mod P,将A通过不安全信道发送给Bob。
Bob生成一个随机数b,并计算B=gb mod P,将B通过不安全信道发送给Alice。
Bob根据收到的A计算出K=Ab mod P ,而Alice根据收到的B计算出K=Ba mod P。
双方得到了相同的K,即gab mod P。K可以用于之后通讯的加密密钥。
可见,这个过程中可能被窃听的只有A,B,而a,b,K是保密的。并且根据A,B,P,g这4个数,不能轻易计算出K,因此K可以作为一个安全的密钥。
当然安全是相对的,该协议的安全性取决于数值的大小,通常a,b,P都选取数百位以上的大整数以避免被破解。然而如果Alice和Bob编程时偷懒,为了避免实现大数运算,选择的数值都小于231,那么破解他们的密钥就比较容易了。
输入
第一行包含两个空格分开的正整数g和P。
第二行为一个正整数n,表示Alice和Bob共进行了n次连接(即运行了n次协议)。
接下来n行,每行包含两个空格分开的正整数A和B,表示某次连接中,被窃听的A,B数值。
输出
输出包含n行,每行一个正整数K,为每次连接你破解得到的密钥。
样例输入
3 31
3
27 16
21 3
9 26
样例输出
4
21
25
提示
对于30%的数据,2≤A,B,P≤1000。
对于100%的数据,2≤A,B<P<231,2≤g<20,1≤n≤20。
题意 :
给你A B g p ,让你去求K k=A^b%p 或者k=B^a%p
就用到了北上广深(BSGS)算法 :https://blog.csdn.net/passer__/article/details/81530354
A=g^a%p 等价于 A%p=g^a%p 等价于A≡g^a(mod p) 就可以套用BSGS算法了
m=sqrt(p) 为啥是sqrt(p)看上博客的那个推算
a=i*m-j; A=g^i*m-j%p 然后 A * g^j = g^(i*m) %p 然后预处理g^j次方用map或者hash。map操作中如果对于重复的数只会是最近的那个j导致的,也就是说j是最大的那个,这样得到的i*m-j也是最小的,完全Ojbk。
然后i从0-m开始乘以g然后判断是否相等 相等就是那个点,这样就可以求出来a 然后快速幂求一下B^a%p就出来K了。
我数学菜啊,真的菜啊。
测试:落谷单个最大 map:220ms hash最大50
bzoj总耗时 map: 868 hash 448
upc 耗时 map:152 hash 52
下是map做法:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll g,p;
ll Qpow(ll n,ll m)
{
n=n%p;
ll ans = 1;
while(m)
{
if(m&1)
ans=(ans*n)%p;
n=(n*n)%p;
m=m>>1;
}
return ans%p;
}
int main()
{
int n;
scanf("%lld%lld",&g,&p);
ll m=sqrt(p)+1;//分块
map<ll,ll>M;//预处理 g^j
ll ans=1;
for(int i=0;i<=m;i++)//预处理到m 最大比p大
{
M[ans]=i;
ans=(ans*g)%p;
}
ll gm=Qpow(g,m);
scanf("%d",&n);
for(int i=0;i<n;i++)
{
ll A,B;
int b;
scanf("%lld%lld",&A,&B);
ll temp=(gm*Qpow(B,p-2))%p;
for(int j=1;j<=m;j++)
{
if(M.count(temp))
{
b=j*m-M[temp];
break;
}
temp=(temp*gm)%p;
}
ll Ans=Qpow(A,b);
printf("%lld\n",Ans);
}
return 0;
}
贴上我kuangbin巨巨的hash模板
#include<bits/stdc++.h>
using namespace std;
const int MOD = 1e5+7;
int hs[MOD],head[MOD],Next[MOD],id[MOD],top;
void insert(int x,int y)
{
int k = x%MOD;
hs[top] = x, id[top] = y, Next[top] = head[k], head[k] = top++;
}
int find(int x)
{
int k = x%MOD;
for(int i = head[k]; i != -1; i = Next[i])
if(hs[i] == x)
return id[i];
return -1;
}
int BSGS(int a,int b,int n)
{
memset(head,-1,sizeof(head));
top = 1;
if(b == 1)return 0;
int m = sqrt(n*1.0), j;
long long x = 1, p = 1;
for(int i = 0; i < m; ++i, p = p*a%n)insert(p*b%n,i);
for(long long i = m; ;i += m)
{
if( (j = find(x = x*p%n)) != -1 )return i-j;
if(i > n)break;
}
return -1;
}
long long Qpow(long long n,long long m,long long p)//快速幂
{
n=n%p;
long long ans = 1;
while(m)
{
if(m&1)
ans=(ans*n)%p;
n=(n*n)%p;
m=m>>1;
}
return ans%p;
}
int main()
{
int g,n;
long long a,b,p;
scanf("%d%lld",&g,&p);
scanf("%d",&n);
while(n--)
{
scanf("%lld%lld",&a,&b);
long long ans = BSGS(g,b,p);
ans=Qpow(a,ans,p);
printf("%lld\n",ans);
}
return 0;
}