bzoj 2242: [SDOI2011]计算器 & BSGS算法笔记
这题的主要难点在于第三问该如何解决
于是就要知道BSGS是怎样的一种方法了
首先BSGS是meet in the middle的一种(戳下面看)
http://m.blog.csdn.net/blog/zentropy/11200099
看完链接后再看以下内容
---------------------------------------------------------------------------------------------------------------------
对于一个质数$p$我们由费马小定理知道$y^{x}mod p$最多$(p-1)$次便是一个循环节
因此如果有解$x$一定在$0$到$p-1$中
所以我们只需知道$x$取$0$到$p-1$是否有解即可
根据meet in the middle 的思想 令$m=\sqrt{p-1}$
那么我们仅需先求出 $x$为$0$到$m-1$的(如果有解这里就退出)
然后再求出 $x$为$m,2m,3m……nm(nm \leq p-1)$
分别询问$y^0$到$y^{m-1}$中是否有和$y^{km}$乘起来$=z(modp)$的
然而显然我们这个操作直接做的话 是$\sqrt{n}*\sqrt{n}=n$的
所以学过逆元怎么求了之后可以将$y^{km}(modp)$意义下的逆元与z相乘
然后再询问$y^0$到$y^{m-1}$中有没有与它相等的即可
这样去做就是$\sqrt{n}$*hash的复杂度
有手动hash技巧的话 hash复杂度可以看做1
比较懒的话 直接用map来hash就是$\log{(n)}$
#include <bits/stdc++.h> using namespace std; map<int,int> mp; int solve1(int x,int y,int mod) { long long t=x,re=1; while(y) { if(y&1) re=re*t%mod; t=t*t%mod; y>>=1; } return (int)re; } int exgcd(int a,int b,int &x,int &y) { if(!b) { x=1; y=0; return a; } int t,d; d=exgcd(b,a%b,x,y); t=x; x=y; y=t-(a/b)*x; return d; } void solve2(int y,int z,int p) { int x,yy; int d=exgcd(y,p,x,yy); if(z%d) { puts("Orz, I cannot find x!"); return; } x=(long long)x*(z/d)%p; x=(x<0?x+p:x); printf("%d\n",x); } bool solve3(int y,int z,int p) { // if(z>=p) // return 0; y%=p; if(!y) { if(z) return 0; puts("1"); return 1; } mp.clear(); int m=ceil(sqrt(p-1)); long long t=1; for(int i=0;i<m;++i) { if(t==z) { printf("%d\n",i); return 1; } if(!mp[t]) mp[t]=i+1; else return 0; t=t*y%p; } int inv=solve1(y,p-1-m,p); t=z; for(int i=m;i<=p-2;i+=m) { t=t*inv%p; if(mp[t]) { printf("%d\n",i+mp[t]-1); return 1; } } return 0; } int main() { int t,ca,y,z,p; scanf("%d%d",&t,&ca); while(t--) { scanf("%d%d%d",&y,&z,&p); if(ca==1) printf("%d\n",solve1(y,z,p)); else if(ca==2) solve2(y,z,p); else if(ca==3) if(!solve3(y,z,p)) puts("Orz, I cannot find x!"); } return 0; }