大步小步算法(BSGS)及扩展 & bzoj 2480

来源:互联网 发布:mac版qq无法接收文件 编辑:程序博客网 时间:2024/06/08 10:37

大步小步算法用于解决离散对数问题:
求满足axy(modp)的最小自然数x,其中ap互质,或者报告无解。

根据欧拉定理,aϕ(p)1(modp),所以,如果有解,必然有一个在[0,ϕ(p))内。为了简单起见,直接考察[0,p1)

让我们运用meet-in-middle的思想。设x=kmr(1rm)m是某个选定的数,那么

(am)kyar(modp)

枚举r=1,2,...,m,将(yarmodp,r)存入一张表。枚举k=1,2,...,ceiling(pm),查询是否存在r使得yar(am)kam用快速幂计算。如果存在,返回kmr;如果从未找到,则无解。

设表的插入和查询的时间复杂度分别是O(f(p))O(g(p)),则本算法的时间复杂度为O(mf(p)+pmg(p)+lgm)。由算术-几何均值不等式,有mf(p)+pmg(p)2pf(p)g(p),等号当且仅当mf(p)=pmg(p)时取得。当使用哈希表或者map时,f(p)=g(p),于是m=p时。因此,对于每一个p,取m=p时算法有最优复杂度。

使用哈希表,BSGS的时间复杂度是O(p);使用map,BSGS的时间复杂度是O(plgp)

以上通过设x=kmr而非km+r避免了求逆元,但是需要逆元存在,ap$互质的条件保证了这一点。

如果ap不互质怎么办呢?
转化。

ax=y+kp

a>1,设g1=gcd(a,p),则y必须含因子g1,否则无解。约去这个g1,得
ag1ax1=yg1+kpg1

然而,也许g2=gcd(a,pg1)>1,那就再把g2约掉
a2g1g2ax2=yg1g2+kpg1g2

重复这个过程,最终得到
angiaxn=ygi+kpgi

用上面的BSGS解决即可。其实这样复杂度已经非常玄学了……

angi可能很大,事实上可以随手modpgi,相当于把一些东西吸收到k里面。这样,可以完全避免逆元的求取。

如果某一步出现angiygi,返回n即可。

一篇很好的文章,以上许多内容源自这里:扩展大步小步法解决离散对数问题

bzoj 2480。注意特判模1。

2016.3.29新加数据一组 by 1430586275

大概加的就是模1吧……包括上面那篇文章中的代码,网上不少题解现在都是无法AC的。

#include <cstdio>#include <cmath>#include <map>using namespace std;typedef long long ll;ll gcd(ll a, ll b){    return b ? gcd(b, a%b) : a;}inline ll fast_exp(ll x, ll n, ll p){    ll z = 1;    for (ll y = x%p; n; y = y*y%p, n >>= 1)        if (n & 1)            z = z*y%p;    return z;}ll extend_bsgs(ll a, ll y, ll p){    a %= p;    y %= p;    if (a == 0)        return y > 1 ? -1 : y == 0 && p > 1;    ll g, c = 0, q = 1;    while ((g = gcd(a, p)) != 1) {        if (y == 1)            return c;        if (y % g)            return -1;        ++c;        y /= g;        p /= g;        q = a/g*q%p;    }    map<ll, ll> x;    ll m = sqrt(p);    for (ll i = 1, t = y*a%p; i <= m; ++i, t = t*a%p)        x[t] = i;    for (ll i = m, t = fast_exp(a, m, p); i-m < p-1; i += m)        if (q = q*t%p, x.count(q))            return i-x[q]+c;    return -1;}int main(){    ll a, p, b;    while (scanf("%lld %lld %lld", &a, &p, &b), p) {        ll ans = extend_bsgs(a, b, p);        if (ans == -1)            puts("No Solution");        else            printf("%lld\n", ans);    }    return 0;}
0 0
原创粉丝点击