1. 程式人生 > 其它 >P3846 [TJOI2007] 可愛的質數/【模板】BSGS 題解

P3846 [TJOI2007] 可愛的質數/【模板】BSGS 題解

BSGS本質上是一個分塊演算法。

我們考慮暴力求解的過程,顯然答案的週期為$p$,所以我們只需要求得p之內的答案即可。

此時依然可能有多個解。

這個時候,我們只需要列舉1到p利用快速冪求值即可。

不過這太慢了,我們考慮使用分塊的方法

我們把p分為$m=\sqrt p$塊

我們求出每一塊的端點值即$a^0,a^m,a^{2m},a^{3m}...a^{(m-1)m}$

對於每一個塊的內部,我們預處理出所有可能的值,即$a^0,a^1...a^m$

列舉每一個端點值,查詢端點值內部有沒有某一個點能夠滿足原題條件。

形式化的請看oi-wiki

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<cmath>
#define int long long
using namespace std;
inline int r()
{
	int s=0,k=1;char c=getchar();
	while(!isdigit(c))
	{
		if(c=='-')k=-1;
		c=getchar();
	}
	while(isdigit(c))
	{
		s=s*10+c-'0';
		c=getchar();
	}
	return s*k;
}
int p,b,n;
int gcd(int a,int b)
{
	if(!b)return a;
	return gcd(b,a%b);
}
map<int,int>m;
int pw(int a,int b,int mod)
{
	int base=a,ans=1;
	while(b)
	{
		if(b&1)
		{
			ans*=base;
			ans%=mod;
		}
		base*=base;
		base%=mod;
		b>>=1;
	}
	return ans;
}
signed main()
{
	p=r();b=r();n=r();
	int g=gcd(b,p);
	if(n%g)
	{
		cout<<"no solution"<<endl;
		return 0;
	}
	n/=g;p/=g;b/=g;
	int s=sqrt(p)+1;
	int now=1;
	for(int i=1;i<=s;i++)//b^i
	{
		now*=b;
		now%=p;
		if(m.find(now)==m.end())m[now]=i;
	}
	for(int i=0;i<=s;i++)//根號分塊 si+j
	{
		int tmp=pw(b,i*s,p);
		tmp=pw(tmp,p-2,p);
		int x=n*tmp;
		x%=p;
		if(m.find(x)!=m.end())
		{
			cout<<i*s+m[x];
			return 0;
		}
	}
	cout<<"no solution"; 
}