其实这个东西挺简单的,就是紫题了。
先放板子题:P2485 [SDOI2011]计算器
操作一二就不说了,反正这里是讲的是(BSGS)。
(BSGS)是解决离散对数问题的,全称(Baby;Step;Gaint;Step)(不是拔山盖世),即求:
[a^xequiv bpmod{p}
]
的最小整数解(x)。
但要保证(pin P)(质数集合)。
那么我们可以发现(x)变化时得到的余数会出现循环节,长度大概是 (leftlceilsqrt{n}
ight
ceil) 的。
那我们令(m= leftlceilsqrt{n}
ight
ceil),
就会存在常数(u,v)使:
[a^{um+v}equiv bpmod{p}
]
即:
[a^{um}equiv ba^{-v}pmod{p}
]
这样需要处理逆元,可有没有好点的方法呢?
不妨设(x=um-v),
我们可以知道:
[a^{um}equiv ba^{v}pmod{p}
]
我们不妨枚举同余号任意一侧(下面代码是右边),存一下(这时要去重),然后枚举另一边,寻找一一对应关系即可。
显然这时枚举两边的上界都是(sqrt{p})的,这时是最小的。
注意这里枚举的边界:(v)是([0,m),u)是((0,m])(由于我们其实是把(u)在本来正常想法上(+1))。
如果有枚举一边得到的两对数有相同的对应值,那贪心的选用编号大的,另一边选编号小的,再二分找一下即可。
然后要注意如果余数是(1),直接得(0)就好了,不知道为啥算不出来。
综上,复杂度是(O(sqrt{p}logsqrt{p})=O(sqrt{p}log p)),可以通过本题,是要比传统的(map)跑得快的。
(上面变量名很清奇......不要吐槽就好)
下面就是代码了:
(Code):
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int t,flag;
ll a,b,p;
ll quickpow(ll a,ll b)
{
ll ans=1,base=a;
while(b)
{
if(b&1) ans=ans*base%p;
b>>=1;
base=base*base%p;
}
return ans%p;
}
ll x,y;
void exgcd(ll a,ll b)
{
if(b==0)
{
x=1,y=0;
return;
}
exgcd(b,a%b);
ll t=x;
x=y;
y=t-a/b*y;
}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll sol(ll a,ll b,ll c)
{
ll g=gcd(a,b);
if(c%g) return -1;
a/=g,b/=g,c/=g;
exgcd(a,b);
x*=c;
while(x<0) x+=b;
x%=b;
return x;
}
struct node
{
int id,val;
}book[320005],dic[320005];
int c=0,cnt=0,h;
bool cmp(node n,node m){if(n.val^m.val) return n.val<m.val;else return n.id<m.id;}
void get(int h,ll a,ll b,ll p)
{
ll now=1;
for(int i=0;i<h;i++)
{
book[++c].val=now*b%p;
now=now*a%p;
book[c].id=i;
}
return;
}
void process()
{
sort(book+1,book+c+1,cmp);
for(int i=1;i<=c;i++)
{
if(book[i].val!=dic[cnt].val) dic[++cnt]=book[i];
else dic[cnt]=book[i];
}
return;
}
ll find(int h,ll a)
{
ll d=quickpow(a,(ll)h),now=1;
for(int i=1;i<=h+1;i++)
{
now=now*d%p;
int l=1,r=cnt;
while(l<r)
{
int mid=(l+r)>>1;
if(now>dic[mid].val) l=mid+1;
else r=mid;
}
if(dic[l].val!=now) continue;
else return ll(i*h-dic[l].id);
}
return -1;
}
int main()
{
scanf("%d%d",&t,&flag);
while(flag==1)
{
if(!t) break;
t--;
scanf("%lld%lld%lld",&a,&b,&p);
printf("%lld
",quickpow(a,b));
}
while(flag==2)
{
if(!t) break;
t--;
scanf("%lld%lld%lld",&a,&b,&p);
b%=p;
ll cur=sol(a,p,b);
if(cur==-1) printf("Orz, I cannot find x!
");
else printf("%lld
",cur);
}
while(flag==3)
{
if(!t) break;
t--;
scanf("%lld%lld%lld",&a,&b,&p);
b%=p;
if(b==1)
{
printf("0
");
continue;
}
h=ceil(sqrt((int)p));
c=0,cnt=0;
memset(book,0,sizeof(book));
memset(dic,0,sizeof(dic));
get(h,a,b,p);
process();
ll cur=find(h,a);
if(cur<0) printf("Orz, I cannot find x!
");
else printf("%lld
",cur);
}
return 0;
}
当然(p)非质数也能解,以后等我学了再说吧。