P4195 【模板】扩展BSGS

题目

题目

思路

先把exBSGS变成普通BSGS。
我们这样做:
a x ≡ b (   m o d     c ) = > a x + c y = b a^x\equiv b(\bmod~c)=>a^x+cy=b axb(mod c)=>ax+cy=b
接下来我们不停地除a,c的最大公约数(d1)。
b 1 = a d 1 a x − 1 + c 1 y b_1={a\over d_1}a^{x-1}+c_1y b1=d1aax1+c1y
我们接下来接着除gcd(a,c1)=d2
b 2 = a 2 d 1 d 2 a x − 2 + c 2 y b_2={a^2\over d_1d_2}a^{x-2}+c_2y b2=d1d2a2ax2+c2y
我们不难发现此时若b不被gcd整除,则无解。
设一共做了 n n n次, D = a n d 1 d 2 … … d n D={a^n\over d_1d_2……d_n} D=d1d2dnan
剩下的 a x − n ≡ b n ⋅ D − 1 (   m o d     c n ) a^{x-n}\equiv b_n·D^{-1}(\bmod~c_n) axnbnD1(mod cn)普通BSGS可解。


接下来考虑普通BSGS。
由指数模的周期性,得若有解,必有c以内的解。
我们考虑优(bi)美(tai)的分块。
n \sqrt n n 块,那么每块就是 m = n m=\sqrt n m=n 个数。
那么这时原式变成
a i m − j ≡ b (   m o d     c ) = > a i m ≡ a j b (   m o d     c ) a^{im-j}\equiv b(\bmod~c)=>a^{im}\equiv a^jb(\bmod~c) aimjb(mod c)=>aimajb(mod c)
这里我们需要枚举 1 < = i < = c , 0 < = j < = c 1<=i<=\sqrt c,0<=j<=\sqrt c 1<=i<=c 0<=j<=c
我们把右式存入hash表里(也可以map,只要数据水),这里一共 O ( n ) O(\sqrt n) O(n )
然后再枚举i,又 O ( n ) O(\sqrt n) O(n )
code:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
#include<queue>
#include<deque>
using namespace std;
//When I wrote this code,God and I unterstood what was I doing 
long long gcd(long long a,long long b)
{
	long long r=a%b;
	while (r)
	{
		a=b,b=r,r=a%b;
	}
	return b;
}
long long exgcd(long long a,long long b,long long &x,long long &y)
{
	if (b==0)
	{
		x=1,y=0;
		return a;
	}
	long long r=exgcd(b,a%b,y,x);
	y-=(long long)(a/b)*x;
	return r;
}
long long inv(long long a,long long m)
{
	long long x,y;
	exgcd(a,m,x,y);
	return (x%m+m)%m;
}
struct fP{
	long long tot,head[(((1<<16)-1)<<2)],nxt[500005],b[500005],v[500005],top,sck[500005];
	void init()
	{
		tot=0;
		while (top) head[sck[top--]]=0;
		return;
	}
	void insert(long long x,long long y)
	{
		long long hask=x%(((1<<16)-1)<<2);
		for (long long i=head[hask];i;i=nxt[i])
		{
			if (b[i]==x)
			{
				v[i]=y;
				return;
			}
		}
		if (!head[hask]) sck[++top]=hask;
		nxt[++tot]=head[hask],head[hask]=tot;
		b[tot]=x,v[tot]=y;
		return;
	}
	long long QuantAsk(long long x)
	{
		long long hask=x%(((1<<16)-1)<<2);
		for (long long i=head[hask];i;i=nxt[i])
		{
			if (b[i]==x) return v[i];
		}
		return -1;
	}
} kyx;
long long BSGS(long long a,long long b,long long c)
{
	if (b==1||c==1) return 0;
	long long cnt=0,Gcd=gcd(a,c),d=1;
	while (Gcd!=1)
	{
		if (b%Gcd!=0) return -1;
		cnt++,b/=Gcd,c/=Gcd;
		d=d*(a/Gcd)%c;
		Gcd=gcd(a,c);
	}
	b=b*inv(d,c)%c;
	kyx.init();
	long long block=ceil(sqrt(c)),p=1;
	for (long long i=0;i<block;i++)
	{
		if (p==b) return i+cnt;
		kyx.insert(p*b%c,i);
		p=p*a%c;
	}
	long long q=p,t;
	for (long long i=block;i-block+1<=c-1;i+=block)
	{
		t=kyx.QuantAsk(q);
		if (t!=-1) return i-t+cnt;
		q=q*p%c;
	}
	return -1;
}
int main()
{
	long long a,b,c;
	while (1)
	{
		scanf("%lld%lld%lld",&a,&b,&c);
		if (a==0&&b==0&&c==0) return 0;
		a%=b,c%=b;
		//if (check(a,b,c)) continue;
		long long wj=BSGS(a,c,b);
		if (wj==-1) puts("No Solution");
		else printf("%lld\n",wj);
	}
	return 0;
}
//Now,only God know
//但行好事,莫问前程 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值