「题解」 [CQOI2018]破解D-H协议

前言

人生中第一道紫题,写一篇题解纪念一下。

BSGS

BSGS(拔山盖世,北上广深 )是什么呢?

它是一种能够在 O ( n   ) O(\sqrt n\space) O(n  )的时间复杂度内求解类似于 a x ≡ b ( m o d p ) a^x \equiv b \pmod{p} axb(modp)的算法。

首先,我们设 x = i p − j x=i\sqrt p-j x=ip j,其中 i , j ∈ [ 0 , p   ] i,j\in [0,\sqrt p\space] i,j[0,p  ]

那么这个式子就变成了 a i p − j ≡ b ( m o d p ) a^{i\sqrt p-j} \equiv b\pmod p aip jb(modp)

两边同时乘上 a j a^j aj,很显然,等式变成了

a i p ≡ b a j ( m o d p ) a^{i\sqrt p} \equiv ba^j\pmod p aip baj(modp)

到了这一步,我们就可以先枚举 j j j,每次把 b a j   m o d   p ba^j\bmod p bajmodp存入哈希表。

然后再枚举 i i i,在哈希表中查找 a i p a^{i\sqrt p} aip ,如果找到了,就说明这个等式成立, x x x就应该是 i p − j i\sqrt p-j ip j,如果枚举完了也没找着,就说说明这个等式不成立。

在这个过程中,我们仅仅枚举了两次,时间复杂度只有区区的 O ( n   ) O(\sqrt n\space) O(n  )

题目大意

看起来这个题目很复杂,但其实就是一个BSGS的模板。

简化一下题意,题目告诉你 g a   m o d   p g^a\bmod p gamodp g b   m o d   p g^b\bmod p gbmodp,让你求 g a b   m o d   p g^{ab}\bmod p gabmodp

观察一下,我们发现
g a b   m o d   p g^{ab}\bmod p gabmodp = ( g a ) b   m o d   p =(g^a)^b\bmod p =(ga)bmodp = ( g a   m o d   p ) b   m o d   p =(g^a\bmod p)^b\bmod p =(gamodp)bmodp = A b   m o d   p = A^b\bmod p =Abmodp

也就是说,只要我们把 b b b求出来,那就万事大吉了。

那么如何求 b b b呢?

观察发现:

B = g b   m o d   P B=g^b\bmod P B=gbmodP B ≡ g b ( m o d P ) B \equiv g^b\pmod P Bgb(modP) g b ≡ B ( m o d P ) g^b \equiv B\pmod P gbB(modP)

这不就是BSGS的模板吗?

我们可以用BSGS求出 b b b,然后把用 b b b求出 A b   m o d   p A^b\bmod p Abmodp

有了这个思路,我们就可以开始写代码了!

代码:

#include <bits/stdc++.h>
using namespace std;
int g,n,p;//在最开始就定义三个全局变量
int qpow(int a,int b){
	//快速幂模板
	int res=1;
	while(b){
		if(b&1) res=(a*res)%p;
		a=(a*a)%p;
		b>>=1;
	}
	return res;
}
int BSGS(int b){
	map<int,int>m;//哈希表可以用map代替
	int t=ceil(sqrt(p));//记得要向上取整
	for(int i=1;i<=t;i++) m[b*qpow(g,i)%p]=i;//枚举j,存入哈希表中
	for(int i=1;i<=t;i++){
		int k=qpow(g,i*t)%p;
		if(m[k]) return i*t-m[k];//如果在哈希表中找到了这个值,就说明找到了b
	}
	return -1;//如果没有找到,就说明无解
}

int main(){
	cin>>g>>p>>n;
	for(int i=1;i<=n;i++){
		int A,B;
		cin>>A>>B;
		int b=BSGS(B);//用BSGS求出b
		cout<<qpow(A,b)%p<<endl;//用快速幂求出K
	}
	return 0;
}

如果把这份代码提交到洛谷上,那么就会出现

请添加图片描述
万紫千红的场面。

我们可以尝试着开longlong。

#define int long long

提交后发现

请添加图片描述

WA没了,就变成了tle。

我们可以使用printfscanf

请添加图片描述

优化了却没有完全优化。

抱着侥幸的心理,我开了O2,结果发现。
请添加图片描述

这玄学的数据!

完整代码(记得开O2):

#include <bits/stdc++.h>
#define int long long
using namespace std;
int g,n,p;//在最开始就定义三个全局变量
int qpow(int a,int b){
	//快速幂模板
	int res=1;
	while(b){
		if(b&1) res=(a*res)%p;
		a=(a*a)%p;
		b>>=1;
	}
	return res;
}
int BSGS(int b){
	map<int,int>m;//哈希表可以用map代替
	int t=ceil(sqrt(p));//记得要向上取整
	for(int i=1;i<=t;i++) m[b*qpow(g,i)%p]=i;//枚举j,存入哈希表中
	for(int i=1;i<=t;i++){
		int k=qpow(g,i*t)%p;
		if(m[k]) return i*t-m[k];//如果在哈希表中找到了这个值,就说明找到了b
	}
	return -1;//如果没有找到,就说明无解
}

signed main(){
	scanf("%lld%lld%lld",&g,&p,&n);
	for(int i=1;i<=n;i++){
		int A,B;
		scanf("%lld%lld",&A,&B);
		int b=BSGS(B);//用BSGS求出b
		printf("%lld\n",qpow(A,b)%p);//用快速幂求出K
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值