要解决的问题:
给定整数
,其中
互质,求一个非负整数
,使得
首先我们先可以了解一下,欧拉定理
设
,其中
,为什么要向上取整呢,因为我们要保证遍历到
,才能保证
被遍历到,保证覆盖到最小正整数解。
则原问题转化为:
即:
使用时空扭曲术! 设
使用大复原术! 得到:
这不是同余方程吗! 通过
,求出
,即
.
最小整数解即为第一个符合条件的
.
关于怎么实现快速查找
,我们需要打一个hash表,用map也行,查表即可
struct hash
{
struct node
{
ll t,next,p;
}a[maxn<<2];
int v[maxn<<2],num,len;
void ins(int p,ll t)
{
ll k=t&maxn;
if(v[k]!=num)
{
v[k]=num;
a[k].next=-1;a[k].t=t;a[k].p=p;
}
else
{
for(;;k=a[k].next)
{
if(t==a[k].t)return ;
if(a[k].next==-1)break;
}
a[k].next=++len;a[len].t=t;a[len].p=p;a[len].next=-1;
}
}
ll check(ll t)
{
ll k=t&maxn;
if(v[k]!=num)return -1;
for(;k!=-1;k=a[k].next)if(a[k].t==t)return a[k].p;
return -1;
}
}h;
这是怎么一回事呢?就是说,我预处理将
加密到
表中,由于
,就可以快速查询出
的值了。
那么
我们代码的流程就是
1.将
预处理进hash表
2.后枚举
,判断是否可行。
代码
ll bsgs(ll A,ll B,ll p)
{
//ll t=1%p;for(ll i=0;i<=100;i++){if(t==B)return i;t=t*A%p;}
ll D=1%p;ll t;
ll m=(int)sqrt(p)+1;
t=1%p;h.len=maxn;
for(int i=0;i<m;i++)
{h.ins(i,t);t=t*A%p;}
//h.ins(m,t);
ll am=t;
for(int i=0;i<=m;i++)
{
ll a=D,b=p,k=B,x,y;
ll d=exgcd(a,b,x,y);
x=((x*(k/d))%(b/d)+(b/d))%(b/d);
ll w=h.check(x);
if(w!=-1)return i*m+w;
D=D*am%p;
}
return -1;
}
算法
本文含两种做法。
若
不互质,怎么办呢?
那我们就想办法让它互质呗。
设
,则
设
若依旧不互质,则设
,则
经过多次运算,得到最后一条互质的柿子
其中
做法一:
由
可知,设
,其中
则原问题转化为:
即:
使用时空扭曲术! 设
使用大复原术! 得到:
最后答案就为
.
回到
即可
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#define ll long long
using namespace std;
const ll maxn=(1<<16)-1;
struct hash
{
struct node
{
ll t,next,p;
}a[maxn<<2];
int v[maxn<<2],num,len;
void ins(int p,ll t)
{
ll k=t&maxn;
if(v[k]!=num)
{
v[k]=num;
a[k].next=-1;a[k].t=t;a[k].p=p;
}
else
{
for(;;k=a[k].next)
{
if(t==a[k].t)return ;
if(a[k].next==-1)break;
}
a[k].next=++len;a[len].t=t;a[len].p=p;a[len].next=-1;
}
}
ll check(ll t)
{
ll k=t&maxn;
if(v[k]!=num)return -1;
for(;k!=-1;k=a[k].next)if(a[k].t==t)return a[k].p;
return -1;
}
}h;
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(!b){x=1;y=0;return a;}
else
{
ll tx,ty;ll d=exgcd(b,a%b,tx,ty);
x=ty;y=tx-(a/b)*ty;
return d;
}
}
ll exbsgs(ll A,ll B,ll p)
{
//ll t=1%p;for(ll i=0;i<=100;i++){if(t==B)return i;t=t*A%p;}
ll D=1%p;ll g=0,d;ll t;
B%=p;if(B==1||C==1)return 0;
while((d=gcd(A,p))!=1)
{
if(B%d)return -1;
B/=d;p/=d;D=D*(A/d)%p;g++;
if(B==D)return g;
}
ll m=(int)sqrt(p)+1;
t=1%p;h.len=maxn;
for(int i=0;i<m;i++)
{h.ins(i,t);t=t*A%p;}
//h.ins(m,t);
ll am=t;
for(int i=0;i<=m;i++)
{
ll a=D,b=p,k=B,x,y;
ll d=exgcd(a,b,x,y);
x=((x*(k/d))%(b/d)+(b/d))%(b/d);
ll w=h.check(x);
if(w!=-1)return i*m+g+w;
D=D*am%p;
}
return -1;
}
int main()
{
ll A,p,B;h.num=0;
while(scanf("%lld%lld%lld",&A,&p,&B)!=EOF)
{
if(!A&&!p&&!B)break;
h.num++;
ll x=exbsgs(A,B,p);
if(x==-1)puts("No Solution");
else printf("%lld\n",x);
}
return 0;
}
做法二:
经过我们机房日日夜夜地辛苦奋战,成功将不用
的方法出炉啦!
设
,其中
则原问题转化为:
即:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#define ll long long
using namespace std;
const int maxn=(1<<16)-1;
struct hash
{
int v[maxn<<2];int num;
struct node
{
ll t;int p,next;
}a[maxn<<2];int len;
hash(){num=0;}
inline void ins(int p,ll t)
{
int k=t&maxn;
if(v[k]!=num)
{
v[k]=num;a[k].next=-1;a[k].p=p;a[k].t=t;
}
else
{
for(;;k=a[k].next)
{
if(t==a[k].t){a[k].p=p;return ;}
if(a[k].next==-1)break;
}
a[k].next=++len;a[len].p=p;a[len].t=t;a[len].next=-1;
}
}
inline int find(ll t)
{
int k=t&maxn,p=-1;
if(v[k]!=num)return -1;
for(;k!=-1;k=a[k].next)
if(t==a[k].t)p=a[k].p;
return p;
}
}h;
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll exbsgs(ll A,ll B,ll p)
{
ll d,D=1%p;ll g=0;B=B%p;
if(B==1||p==1)return 0;
while((d=gcd(A,p))!=1)
{
if(B%d)return -1;
B/=d;p/=d;D=(D*A/d)%p;
g++;if(B==D)return g;
}
ll t=1%p;ll m=(int)sqrt(p)+1;
h.len=maxn;
for(int i=0;i<m;i++)
{h.ins(i,(t*B)%p);t=t*A%p;}
ll am=t;
for(int i=0;i<=m;i++)
{
ll w=h.find(D);
if(w!=-1&&i*m-w>=0)return i*m-w+g;
D=D*am%p;
}
return -1;
}
int main()
{
ll a,b,p;
while(scanf("%lld%lld%lld",&a,&p,&b)!=EOF)
{
if(!a&&!b&&!p)break;h.num++;
ll ans=exbsgs(a,b,p);
if(ans!=-1)printf("%lld\n",ans);
else puts("No Solution");
}
return 0;
}
其实每次加密时,我们只要插入 ,查表即可