洛谷 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\)
所以这个题特判下\(k,M\)互不互质然后跑\(BSGS\)就好了
简单说下\(BSGS\)怎么写吧
设\(n=\left \lfloor \sqrt M \right \rfloor,x=i\times n-j,1\le i,j\le n\),原方程就变为
然后把\(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;
}