[BZOJ5296][CQOI2018]破解D-H协议(BSGS模板题)

5296: [Cqoi2018]破解D-H协议

Time Limit: 10 Sec   Memory Limit: 512 MB
Submit: 23   Solved: 16

Description

    Diffie-Hellman密钥交换协议是一种简单有效的密钥交换方法。它可以让通讯双方在没有事先约定密钥(密码)的情况下通过不安全的信道(可能被窃听)建立一个安全的密钥K,用于加密之后的通讯内容。
    假定通讯双方名为Alice和Bob,协议的工作过程描述如下(其中mod表示取模运算):
    1.协议规定一个固定的质数P,以及模P的一个原根g。P和g的数值都是公开的,无需保密。
    2.Alice生成一个随机数a,并计算A=g^a mod P,将A通过不安全信道发送给Bob。
    3.Bob生成一个随机数b,并计算B=g^b mod P,将B通过不安全信道发送给Alice。
    4.Bob根据收到的A计算出K=A^b mod P,而Alice根据收到的B计算出K=B^a mod P。
    5.双方得到了相同的K,即g^(a*b) mod P。K可以用于之后通讯的加密密钥。
    可见,这个过程中可能被窃听的只有A、B,而a、b、K是保密的。并且根据A、B、P、g这4个数,不能轻易计算出K,因此K可以作为一个安全的密钥。
    当然安全是相对的,该协议的安全性取决于数值的大小,通常a、b、P都选取数百位以上的大整数以避免被破解。然而如果Alice和Bob编程时偷懒,为了避免实现大数运算,选择的数值都小于2^31,那么破解他们的密钥就比较容易了。

Input

    输入文件第一行包含两个空格分开的正整数g和P。
    第二行为一个正整数n,表示Alice和Bob共进行了n次连接(即运行了n次协议)。
    接下来n行,每行包含两个空格分开的正整数A和B,表示某次连接中,被窃听的A、B数值。
     2AB<P<2312g<20n<=20 2 ≤ A , B < P < 2 31 , 2 ≤ g < 20 , n <= 20

Output

    输出包含n行,每行1个正整数K,为每次连接你破解得到的密钥。

Sample Input

3 31
3
27 16
21 3
9 26

Sample Output

4
21
25

思路&&分析

    这题就是裸的BSGS模板题,对于 Aga A ≡ g a (mod p),我们可以用BSGS来求出a,从而直接求 Ba B a 就行了。接下来来讲讲BSGS是怎么使用的。

BSGS定义

    BSGS,就是平常所说的大步小步算法,这个算法可以较高效地求出形如同余方程 AxB A x ≡ B (mod C)(C是质数)的解。

如何实现

    我们可以使用分块来优化暴力枚举,对于 AxB A x ≡ B (mod C),我们可以令 m=C m = ⌈ C ⌉ x=im+j x = i ∗ m + j ,那么该式子就可以写成 AimAjB A i ∗ m ∗ A j ≡ B (mod C),于是我们可以将 Aj A j 进行预处理,表示达到这个值的指数项为多少,存到一个哈希表或者map里,之后我们只要枚举 Aim A i ∗ m 就行了,大致复杂度是 O(nlogn) O ( n l o g n ) 。(知道了这个,这题就做完了qwq)

Code

#pragma GCC optimize(3)
#include
#include
using namespace std;
typedef long long ll;
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
/*================Header Template==============*/
mapint>num;
ll g,a,b,p,A,B;
int T,n;
inline void exgcd(ll a,ll b,ll &d,ll &x,ll &y) {
    if(!b)
        d=a,x=1,y=0;
    else
        exgcd(b,a%b,d,y,x),y-=x*(a/b);
}
inline ll inv(ll x) {
    ll d,xx,y;
    exgcd(x,p,d,xx,y);
    return d==1?(xx+p)%p:-1;
}
inline ll pw(ll a,ll b) {
    ll res=1;
    while(b) {
        if(b&1)
            res=res*a%p;
        a=a*a%p;
        b>>=1;
    }
    return res;
}
inline ll solve(ll x) {
    for(int i=0;i<=n;i++) {
        ll v=x*inv(pw(g,i*n))%p;
        if(num.count(v))
            return num[v]+i*n;
    }
    return -1;
}
int main() {
    read(g),read(p),read(T);
    n=ceil(sqrt(p));
    ll x=1;
    num[1]=0;
    for(int i=1;i<=n;i++) {
        x=x*g%p;
        if(!num[x])
            num[x]=i;
    }
    while(T--) {
        read(A),read(B);
        printf("%lld\n",pw(A,solve(B)));
    }
}

你可能感兴趣的:(数论,BSGS)