洛谷 P4861 按钮

房间的铁门上有一个按钮,还有一个显示屏显示着“1”。
旁边还有一行小字:“这是一个高精度M进制计算器,每按一次按钮,屏幕上的数便会乘以K。当个位数再次变为1时,门就开了。”
由于Ada急于出去,所以你要在1s之内求出她的最小按键次数。

首先我们看到题目,每次在\(M\)进制下对\(1\)\(k\),也就是对\(k\)乘方,最后还是得到\(1\)

这样我们就可以列出同余方程\(k^x\equiv 1\ (mod\ M)\)

而初始状态为\(1\),即\(k^0=1\)

所以答案就是满足方程的最小的正整数\(x\)

其实这道题是\(\text{Ex\_BSGS}\),因为题目不满足\(k\)\(M\)互质

考虑我们在做\(\text{Ex\_BSGS}\)时,每次取出\(g=gcd(k,M),b'=\frac{b}{g},M'=\frac{M}{g},k'=\frac{k}{g}\)

得到新方程\(k^{x-1}\times k'\equiv b'\ (mod\ M')\)

移项得到\(k^{x-1}\equiv \frac{b'}{k'}\ (mod\ M')\)

无解的情况就是\(g\nmid b\)并且\(b\ne1\)

证明就不证了,不会的可以去看这篇文章QAQ

直到\(g=1\),即\(k\)\(M\)互质,就可以用\(BSGS\)求解了

而题目给的\(b\)\(1\),那么我们回去看这个过程,\(k,M\)不互质说明\(g>1\)

\[g>1,x\ne0\Rightarrow g\nmid b\Rightarrow \text{无解} \]

所以这个题特判下\(k,M\)互不互质然后跑\(BSGS\)就好了

简单说下\(BSGS\)怎么写吧

\(n=\left \lfloor \sqrt M \right \rfloor,x=i\times n-j,1\le i,j\le n\),原方程就变为

\[k^{i\times n-j}\equiv 1\ (mod\ M) \]

\[(k^n)^i\equiv k^j\ (mod\ M) \]

然后把\(k^j\)求出来哈希一下就可以啦

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
#include <cmath>
#define int long long
using namespace std;
int m,k;
map <int,int> f;
int mypow(int a,int x,int p)   //快速幂
{
	int s=1;
	while (x)
	{
		if (x&1)s=s*a%p;
		a=a*a%p;
		x>>=1;
	}
	return s;
}
int gcd(int a,int b)    //最大公约数
{
	if (!b)return a;
	return gcd(b,a%b);
}
signed main()
{
	cin>>m>>k;
	if (gcd(m,k)!=1)     //特判无解
	{
		cout<<"Let's go Blue Jays!"<<endl;
		return 0;
	}
	int n=ceil(sqrt(m)),b=1;
	for (int i=1;i<=n;i++)    //k^j
	{
		b=b*k%m;
		f[b]=i;
	}
	b=1;
	int tmp=mypow(k,n,m);
	for (int i=1;i<=n;i++)    //(k^n)^i
	{
		b=b*tmp%m;
		if (f[b]&&(i*n-f[b]+m)%m!=0)   //有解且不为0
		{
			cout<<(i*n-f[b]+m)%m<<endl;
			return 0;
		}
	}
	cout<<"Let's go Blue Jays!"<<endl;   //无解
	return 0;
}
posted @ 2020-06-08 20:12  eee_hoho  阅读(98)  评论(0编辑  收藏  举报