离散对数-BSGS算法

口胡

参考:扩展大步小步法解决离散对数问题

离散对数主要是求解这样一类问题:

a x ≡ b ( m o d m ) a^x \equiv b \pmod m axb(modm) 大概就是 ( m o d m ) \pmod m (modm)意义下的对数

这里只考虑 ( m , a ) (m,a) (m,a)为1的情况。一般来说,给出的m是一个质数。

x = A ⌈ p ⌉ + B x = A \lceil \sqrt{p} \rceil + B x=Ap +B,其中 0 ≤ B < ⌈ p ⌉ 0 \leq B < \lceil \sqrt{p} \rceil 0B<p 0 ≤ A < ⌈ p ⌉ 0 \leq A < \lceil \sqrt{p} \rceil 0A<p 。则方程变成

a A ⌈ p ⌉ + B ≡ b ( m o d p ) a^{A\lceil \sqrt{p} \rceil + B} \equiv b \pmod p aAp +Bb(modp)
两边同时乘以 A ⌈ p ⌉ A \lceil \sqrt{p} \rceil Ap 的逆元,则方程变为
a B ≡ b ⋅ a − A ⌈ p ⌉ ( m o d p ) a^{B} \equiv b\cdot a^{-A\lceil \sqrt{p} \rceil} \pmod p aBbaAp (modp)
由于 A A A, B B B都是 O ( p ) O(\sqrt{p}) O(p )级别的数,可以预处理出左边的所有值,然后按从小到大的顺序枚举 A A A,查找左边是否有值对应。采用手写 h a s h hash hash或者C++11的 u n o r d e r e d unordered unordered_ m a p map map就可以 O ( 1 ) O(1) O(1)时间查询。这样的复杂度是 O ( p + p ) O(\sqrt{p} + \sqrt{p}) O(p +p )的。

如果需要对 n n n b b b求解这个模方程,考虑将 x x x 表示为 A ∗ K + B A*K + B AK+B,则 A ≤ ⌊ p k ⌋ A \leq \lfloor \frac{p}{k}\rfloor Akp,预处理左边之后,只需要在右边重复查找 n n n次即可。时间复杂度为 O ( K + ⌊ p k ⌋ ∗ n ) O(K + \lfloor \frac{p}{k}\rfloor * n) O(K+kpn)

code:

对于 x y = n m o d    m x^y = n \mod m xy=nmodm 求解 y y y

LL discrete_log(int x,int _n,int m){
	unordered_map<LL,int>rec;
	int s=(int)(sqrt((double)m));
	for(; (LL)s*s<=m;)++s;
	LL cur=1;
	for(int i=0;i<s;++i){
		rec[cur]=i;
//		cur=cur*x%m;
		MUL(cur,x,m);
	}
	LL mul=cur;
	cur=1;
	for(int i=0;i<s;++i){
		LL more=(LL)_n;
		MUL(more,qpow(cur,m-2,m),m);
		if(rec.count(more)){
			return i*s +rec[more];
		}
//		cur= cur*mul%m;
		MUL(cur,mul,m);
	}
	return -1;
}

zoj 18.1月赛 E

#include

using namespace std;

#define lson rt<<1
#define rson rt<<1|1

typedef long long ll;
const int mod = 1e9+7;
const int _mod = mod - 1;
const int _P = 5;
const int maxn = 1e5+10;

ll a[maxn],Log[maxn];

struct node{
    int l,r;
    ll sum,add,mul;
}tr[maxn<<2];

void Add(ll& x,ll y,int P){
    x+=y;
    if(x>=P) x -= P;
}

void Mul(ll& x,ll y,int P){
    x *= y;
    if(x>=P) x %= P;
}

ll qpow(ll a,ll b,ll P){
    ll ret = 1;
    while(b){
        if(b&1) Mul(ret,a,P);
        Mul(a,a,P);
        b>>=1;
    }
    return ret;
}

void up(int rt){
    tr[rt].sum = tr[lson].sum + tr[rson].sum;
    if(tr[rt].sum >= _mod) tr[rt].sum -= _mod;
}

void down(int rt){
    int m = (tr[rt].l + tr[rt].r)>>1;
    // cout<<"down "<
    // cout<
    if(tr[rt].mul > 1){
        int c = tr[rt].mul;
        Mul(tr[lson].mul,c,_mod);
        Mul(tr[rson].mul,c,_mod);
        Mul(tr[lson].sum,c,_mod);
        Mul(tr[rson].sum,c,_mod);
        Mul(tr[lson].add,c,_mod);
        Mul(tr[rson].add,c,_mod);
        tr[rt].mul = 1;
    }
    if(tr[rt].add > 0){
        int c = tr[rt].add;
        Add(tr[lson].add,c,_mod);
        Add(tr[rson].add,c,_mod);
        Add(tr[lson].sum,(ll)(m-tr[rt].l + 1) * c % _mod,_mod);
        Add(tr[rson].sum,(ll)(tr[rt].r - m) * c % _mod,_mod);
        tr[rt].add = 0;
    } 
}

void build(int rt,int L,int R){
    // cout<
    tr[rt].l = L;
    tr[rt].r = R; 
    tr[rt].add = 0;
    tr[rt].mul = 1;
    if(L == R){
        tr[rt].sum = a[L];
        return ;
    }
    int m = (L+R)>>1;
    build(lson,L,m);
    build(rson,m+1,R);
    up(rt);
}

void update(int rt,int L,int R,int v,int k){
    if(L <= tr[rt].l && tr[rt].r <= R){
        if(k == 1){            
            Add(tr[rt].sum,(ll)(tr[rt].r - tr[rt].l + 1) * v % _mod,_mod);
            Add(tr[rt].add,v,_mod);
        }
        else{
            Mul(tr[rt].sum,v,_mod);
            Mul(tr[rt].mul,v,_mod);
            Mul(tr[rt].add,v,_mod);
        }
        return ;
    }
    down(rt);
    int m = (tr[rt].l + tr[rt].r) >> 1;
    if(L <= m) update(lson,L,R,v,k);
    if(m < R) update(rson,L,R,v,k);
    up(rt);
}

ll query(int rt,int L,int R){
    // cout<<"query "<
    if(L <= tr[rt].l && tr[rt].r <= R) return tr[rt].sum;
    down(rt);
    int m = (tr[rt].l + tr[rt].r) >> 1;
    ll ret = 0;
    if(L <= m) Add(ret,query(lson,L,R),_mod);
    if(m < R) Add(ret,query(rson,L,R),_mod);
    return ret;
}

const int K = 300000;
unordered_map<ll,int> rec;
ll Base;

ll get_log(int x,int _n,int m){
	ll mul = Base;
	ll cur=1;
	for(int i=0;i<K;++i){
		ll more=(ll)_n;
		Mul(more,qpow(cur,m-2,m),m);
		if(rec.count(more)){
			return i*K + rec[more];
		}
		Mul(cur,mul,m);
	}
	return -1;
}

bool check[2005];

void init(){
    memset(check,false,sizeof(check));
	ll cur = 1;
	for(int i = 0;i<K;i++){
		rec[cur] = i;
		Mul(cur,_P,mod);
	}
	Base = cur;
    Log[1] = get_log(_P,1,mod);
    for(int i = 2;i< 1005;i++){
        if(!check[i]){
            Log[i] = get_log(_P,i,mod) % _mod;
        }
        for(int j = i * 2; j < 1005;j += i){
            check[j] = true;
            Log[j] = Log[j/i] + Log[i];
            if(Log[j] >= _mod) Log[j] -= _mod;
        }
    }
}

int main(){
    init();
    int cas; scanf("%d",&cas);
    while(cas--){
        int n,q;
        scanf("%d%d",&n,&q);
        for(int i = 1;i<=n;i++){
            scanf("%lld",&a[i]);
            a[i] = Log[a[i]];
        }
        build(1,1,n);
        // cout<<"build ok"<
        while(q--){
            int op,l,r,v,k;
            scanf("%d",&op);
            if(op == 1){
                scanf("%d%d%d",&l,&r,&v);
                update(1,l,r,Log[v],1);
                // update(1,l,r,v,1);
            }
            else if(op == 2){
                scanf("%d%d%d",&l,&r,&k);
                update(1,l,r,k,2);
            }
            else {
                scanf("%d%d",&l,&r);
                ll ans = query(1,l,r);
                ans = qpow(_P,ans,mod);
                printf("%lld\n",ans);
            }
        }
    }
    return 0;
}

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