【SDOI2011】【BZOJ2242】计算器

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
xynp=z
明显的exgcd形式
t=gcd(y,p) ,如果z是t的倍数才有解,原因百科上有讲。
然后 y,p,z 分别除以 t ,这样 y 就与 p 互质了,那么新的 y,pgcd(y,p)=1 ,所以可以用exgcd来解决 xynp=gcd(y,p)=1 这个情况。
但是后面是等于1不是等于z,怎么办。
直接用解出来的x乘上z就可以了,因为
xynp=1
后面乘上z
xyznpz=z
所以 y(xz)p(nz)=z
那么答案自然就是xz了。然而要输出最小非负整数。
那么就一直用xz+p,直到得出的ans>=0为止。
因为 y(xz+p)p(nz+y)=z
拆开括号:
xyz+ypnpzyp=z
yp正负抵消。
所以,一般ax+by=z得出的一种解,要得出其他的解让x不断+b,y不断-a就可以了。
第三项任务:BSGS裸题,参考BSGS算法学习小记

Code

#include
#include
#include
#include
#include 
#include
#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);
    }
}

你可能感兴趣的:(扩展欧几里得,数论,省选,BSGS)