您的位置:首页 > 其它

【SDOI2011】【BZOJ2242】计算器

2016-08-01 12:09 381 查看

Description

你被要求设计一个计算器完成以下三项任务:

  1、给定 y、z、p,计算y^z mod p的值;

  2、给定 y、z、p,计算满足xy≡z(mod p)的最小非负整数 ;

  3、给定y、z、p,计算满足y^x≡z(mod p)的最小非负整数 。

Solution

一道裸的数论题。

∙第一项任务,很简单快速幂。

∙第二项任务,运用exgcd:首先式子可以转变成:

xy=np+z

xy−np=z

明显的exgcd形式

设t=gcd(y,p),如果z是t的倍数才有解,原因百科上有讲。

然后y,p,z分别除以t,这样y就与p互质了,那么新的y,p上gcd(y,p)=1,所以可以用exgcd来解决xy−np=gcd(y,p)=1这个情况。

但是后面是等于1不是等于z,怎么办。

直接用解出来的x乘上z就可以了,因为

xy−np=1

后面乘上z

xyz−npz=z

所以y∗(xz)−p∗(n∗z)=z

那么答案自然就是xz了。然而要输出最小非负整数。

那么就一直用xz+p,直到得出的ans>=0为止。

因为y∗(xz+p)−p∗(n∗z+y)=z

拆开括号:

xyz+yp−npz−yp=z

yp正负抵消。

所以,一般ax+by=z得出的一种解,要得出其他的解让x不断+b,y不断-a就可以了。

∙第三项任务:BSGS裸题,参考BSGS算法学习小记

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
ll i,j,k,l,t,n,m,ans,x,y,p;
map<int,int>a;
ll qsm(ll x,ll y){
ll z=1;
for(;y;y/=2,x=x*x%p)if(y&1)z=z*x%p;
return z;
}
ll gcd(ll x,ll y){return (!y)?x:gcd(y,x%y);}
void exgcd(ll a,ll b,ll &x,ll &y){
if(!b){x=1,y=0;return;}
exgcd(b,a%b,x,y);
ll t=x;x=y;y=t-a/b*y;
}
void cheng(ll y,ll z){
p=-p;
ll t=gcd(y,p);
if(z%t){printf("Orz, I cannot find x!\n");return;}
y/=t,p/=t,z/=t;
ll a,b;exgcd(y,p,a,b);
a=a*z%p;
while(a<0)a+=p;
printf("%d\n",a);
}
void BSGS(ll x,ll z){
ll i,j,k=1,y=z%p;x%=p;
if(!x&&!z){printf("1\n");return;}
if(!x){printf("Orz, I cannot find x!\n");return;}
ll m=ceil(sqrt(p-1));
ll ni=qsm(x,m);ni=qsm(ni,p-2);
a.clear();
a[1]=m+1;
fo(j,1,m-1){
k=k*x%p;
if(!a[k])a[k]=j;
}
fo(i,0,m-1){
ll u=a[y];
if(u){
if(u==m+1)printf("%lld\n",i*m);
else printf("%lld\n",i*m+u);
return;
}
y=y*ni%p;
}
printf("Orz, I cannot find x!\n");
}
int main(){
scanf("%lld%lld",&n,&m);
fo(i,1,n){
scanf("%lld%lld%lld",&x,&y,&p);
if(m==1)printf("%lld\n",qsm(x,y));
else if(m==2)cheng(x,y);
else BSGS(x,y);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: