• 高次同余方程 $BSGS$


    第一篇(Blog)...

    还是决定把(luogu)上的那篇搬过来了。


    BSGS,又名北上广深

    它可以用来求(a^x equiv b (mod n))这个同余方程的一个解,其中(a,n)互质。

    欧拉定理告诉我们,这里(a^{varphi(n)} equiv 1 (mod n))

    由于(a^0 equiv 1 (mod n)),所以这里(x)(varphi(n))(a^x mod n)就开始循环了。

    所以我们最坏情况就是(n)为素数时,从(0)(n-1)枚举(x)就行了。

    这样我们就得到了一个(O(n))复杂度的优秀算法。

    然而(n < 2^{31})......

    我们考虑让(x = im - j(0 le j le m)),即把(0...n-1)(n)个数按每块大小为(m)分块。

    就有

    [a^{im - j} equiv b (mod n) ]

    两边同时乘(a^j)

    [a^{im} equiv ba^j (mod n) ]

    对于等式右边,总共只会有(m+1)种不同的(j),我们把(ba^0,ba^1,...,ba^m)全塞到一个(map)里,(i)也只会有(lceil frac{n}{m} ceil)种取值,直接暴力。

    最后复杂度为(O(m + lceilfrac{n}{m} ceil))

    (m = lceil sqrt{n} ceil),就可以做到(O(sqrt{n}))

    当然,用(map)的话还要乘上一个(log)

    其实分块的时候(j)取到(m)可能会导致有些(x)被考虑到两次,但并不影响,而且边界还不怎么需要处理。

    贴一下Luogu P3846(板子题)的代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    int fpow(int a, int b, int c){
    	int ret = 1;
    	for (a %= c; b; b>>=1, a = 1ll*a*a % c) if (b&1) ret = 1ll * ret * a % c;
    	return ret;
    }
    
    int BSGS(int a, int b, int n, int &ret) {
    	int m = ceil(sqrt(n));
    	map<int,int> h;
    	for (int i = 0, tmp = b%n; i <= m; i++, tmp = 1ll*tmp*a%n)
    		h[tmp] = i;
    	a = fpow(a, m, n);
    	for (int tmp = a, i = 1; i <= m; i++, tmp = 1ll*tmp*a%n)
    		if (h.count(tmp)) { ret = 1ll*i*m - h[tmp]; return 1; }
    	return 0;
    }
    
    int main(){
    	int a, b, n, flg, ans; scanf("%d%d%d", &n, &a, &b);
    	flg = BSGS(a, b, n, ans);
    	if (!flg) puts("no solution"); else printf("%d
    ", ans);
    	return 0;
    }
    

    还有比较毒瘤的就是如果(a equiv 0 (mod n))的时候,需要特判(b otequiv 0 (mod n))

    因为如果(a)(n)的倍数,那怎么乘都是(0)...

    所以板子在这里:

    int BSGS(int a, int b, int n, int &ret) {
    	a %= n, b %= n;
    	if (a == 0) { if (b == 0) { ret = 0; return 1; } else return 0; }
    	int m = ceil(sqrt(n)); map<int,int> h;
    	for (int tmp = b%n, i = 0; i <= m; i++, tmp = 1ll*tmp*a % n) h[tmp] = i;
    	a = fpow(a, m, n);
    	for (int tmp = a%n, i = 1; i <= m; i++, tmp = 1ll*tmp*a % n)
    		if (h.count(tmp)) { ret = 1ll*i*m - h[tmp]; return 1; }
    	return 0;
    }
    

    (ExBSGS)的话。。。改天学吧 感觉也没什么用

  • 相关阅读:
    asp.net图片上传代码
    C#(同步调用、异步调用、异步回调)
    跨线程的控件调用
    C# ListView用法详解 很完整
    获取dataGridView双击时判断双击的是下面的行,还是列头
    加密解密类
    C# 中DataGridView 绑定List<T>做数据源的操作问题
    C#获取当前程序集的完整路径
    接口测试-小结
    接口测试用例
  • 原文地址:https://www.cnblogs.com/wxq1229/p/12207157.html
Copyright © 2020-2023  润新知