zoukankan      html  css  js  c++  java
  • BSGS(baby step gaint step)+快速幂+exgcd逆元

    1. 一个神奇的算法, 快速求出 A^x=B(mod C),C是素数,求最小的非负x值 若x有解,那么 0<=x < C, 为什么?

      由费马小定理可以得a^(c-1) = 1(mod c)
      那么0~c-1必定是一个循环节(不一定是最小的)。既然是%c,那么B一定是0到c-1之间的一个数。最坏的条件下,a的c-1以内次方%c的余数各不相同,那么在0~C-1时一定存在一个x满足条件。
      那么不是最坏情况时, 就能找到一个更小的在0~c-1的循环节。

      BSGS的算法是这样的:

      首先取m=sqrt(c)向上取整。(为什么取sqrt(c)?我也不是很懂)

      然后先预处理a的0到m次方。

      a^x=b ( %c ) 设x=i*m+j; 即: i为x/m,j为x%m。 a^(i*m+j)=b; b * (a^(-m))^i =
      a^j ( %c )

      先枚举j,把右边存起来(Hash 下一步用二分查找, 或哈希表) 枚举i,如果左边的数值曾经存储过(b * (a^(-m))^i =
      a^j),则 x=i*m+j, 得解。

    2. 快速幂, 其实很好理解, 对于求x^y mod p
      我们可以把每次的乘积存下来, 最后一起累乘, 即
      2^10
      = 2^5 * 2^5
      那么每次把y>>=1, 如果y是奇数那么就把这次分出的答案乘进答案
    3. xy mod p = z mod p
      求最小的x, 其实很简单, 可以将方程变成ax+by=c
      同时除以gcd(a, b), 如果c除不尽那么无解
      否则变成a0x+b0y=c0;->(a0x+b0y=1;x,y乘以c0)

    下面给一个例题;
    给出下列三种操作
    1、给定 y、z、p,计算 y^z mod p 的值;
    2、给定 y、z、p,计算满足 xy ≡z(mod p)的最小非负整数 x;
    3、给定 y、z、p,计算满足 y^x ≡z(mod p)的最小非负整数 x.
    三个操作都在上面讲过了下面给出代码

    #include <bits/stdc++.h>
    using namespace std;
    
    #define hash Hash
    #define rep(i, s, t) for(int i = s; i <= t; ++i)
    
    typedef long long ll;
    
    ll p;
    ll calc1(ll x, ll y) {
        ll res = 1LL;
        for(; y; y>>=1) {if(y & 1) res = res*x % p; x = x*x % p;}
        return res;
    }
    
    ll gcd(ll a, ll b) {
        while(b) {
            ll t = a;
            a = b;
            b = t%b;
        }
        return a;
    }
    
    void exgcd(ll a, ll b, ll &x, ll &y) {
        if(!b) {
            x = 1; y = 0;
        }
        else {
            exgcd(b, a%b, y, x);
            y -= a/b*x;
        }
    }
    
    struct HASH_TABLE {
        const ll mod = 100007;
        ll h[100007+10][2];
    
        ll find(ll x) {
            ll t = x % mod;
            while(h[t][0] != x && h[t][0] != -1) t = (t+1) % mod;
            return t;
        }
    
        void insert(ll x, ll id) {
            ll t = find(x);
            if(h[t][0] == -1)
                h[t][0] = x, h[t][1] = id;
        }
    
        ll get(ll x) {
            ll t = find(x);
            return h[t][1];
        }
    
        void BSGS(ll a, ll b, ll p) {
            memset(h, -1, sizeof(h));
            ll m = ceil(sqrt(p)), t = 1;
            rep(i, 0, m-1) {
                insert(t, i);
                t = t*a%p;
            }
            ll d=1, x, y;
            ll ans = -1;
            rep(i, 0, m-1) {
                exgcd(d, p, x, y);
                x = ((x*b) % p + p)%p;
                y = get(x);
                //printf("%lld,%lld
    ",x,y);
                if(y ^ -1) {
                    ans = i*m+y;
                    break;
                }
                d = d*t % p;
            }
            if(ans == -1) puts("Orz, I cannot find x!");
            else printf("%lld
    ", ans);
        }
    }T;
    
    int main() {
    #ifndef ONLINE_JUDGE
        freopen("calc.in", "r", stdin);
        freopen("calc.out", "w", stdout);
    #endif
        int _, cmd;
        scanf("%d%d", &_, &cmd);
        while(_--) {
            ll y, z, a, b;
            scanf("%lld%lld%lld", &y, &z, &p);
            if(cmd == 1) cout << calc1(y, z) << endl;
            else if(cmd == 2) {
                ll Gcd = gcd(y, p);
                if(z % Gcd)
                    puts("Orz, I cannot find x!");
                else {
                    z %= p;
                    y /= Gcd; p /= Gcd; z /= Gcd;
                    exgcd(y, p, a, b);
                    a = (a * z % p + p) % p;
                    printf("%lld
    ", a);
                }
            }
            else {
                if(y%p==0) {
                    if(z==1) printf("0
    "); else puts("Orz, I cannot find x!");
                    continue;
                }
                T.BSGS(y, z, p);
            }
        }
        return 0;
    }
  • 相关阅读:
    杜教筛刷题总结
    后缀自动机刷题总结
    回文自动机刷题总结
    后缀数组刷题总结
    LCT刷题总结
    省选模拟一题解
    FFT/NTT中档题总结
    二项式反演总结
    JS只能输入数字,数字和字母等的正则表达式
    jquery 条件搜索某个标签下的子标签
  • 原文地址:https://www.cnblogs.com/pbvrvnq/p/8530165.html
Copyright © 2011-2022 走看看