【BSGS】POJ2417[Discrete Logging]&POJ3243[Clever Y]题解

POJ2417

题目概述

求满足 AxB(mod C) 的最小x,C是素数。

解题报告

这就是经典的BSGS,由于要求最小的,所以哈希表储存时刷个小的就行了。

示例程序

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long LL;
const int MAXINT=((1<<30)-1)*2+1;

int A,B,C;
struct Hashmap //哈希表代替map
{
    static const int Ha=999917,maxe=46340;
    int E,lnk[Ha],son[maxe+5],nxt[maxe+5],w[maxe+5];
    int top,stk[maxe+5];
    void clear() {E=0;while (top) lnk[stk[top--]]=0;}
    void Add(int x,int y) {son[++E]=y;nxt[E]=lnk[x];w[E]=MAXINT;lnk[x]=E;}
    bool count(int y)
    {
        int x=y%Ha;
        for (int j=lnk[x];j;j=nxt[j])
            if (y==son[j]) return true;
        return false;
    }
    int& operator [] (int y)
    {
        int x=y%Ha;
        for (int j=lnk[x];j;j=nxt[j])
            if (y==son[j]) return w[j];
        Add(x,y);stk[++top]=x;return w[E];
    }
};
Hashmap f;

int exgcd(int a,int b,int &x,int &y)
{
    if (!b) {x=1;y=0;return a;}
    int r=exgcd(b,a%b,x,y),t=x;x=y;y=t-a/b*y;
    return r;
}
int BSGS(int A,int B,int C)
{
    if (C==1) if (!B) return A!=1; else return -1;
    if (B==1) if (A) return 0; else return -1;
    if (A%C==0) if (!B) return 1; else return -1; //几种特判
    int m=ceil(sqrt(C)),D=1,Base=1;f.clear();
    for (int i=0;i<=m-1;i++) //先把A^j存进哈希表
    {
        f[Base]=min(f[Base],i);
        Base=((LL)Base*A)%C;
    }
    for (int i=0;i<=m-1;i++)
    {
        int x,y,r=exgcd(D,C,x,y);
        x=((LL)x*B%C+C)%C; //扩欧求A^j
        if (f.count(x)) return i*m+f[x]; //找到了
        D=((LL)D*Base)%C;
    }
    return -1;
}
int main()
{
    freopen("program.in","r",stdin);
    freopen("program.out","w",stdout);
    while (~scanf("%d%d%d",&C,&A,&B))
    {
        int ans=BSGS(A,B,C);
        if (ans==-1) printf("no solution\n"); else
        printf("%d\n",ans);
    }
    return 0;
}

POJ3243

题目概述

求满足 AxB(mod C) 的最小x。

解题报告

经典的扩展BSGS。

示例程序

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long LL;
const int MAXINT=((1<<30)-1)*2+1;

int A,B,C;
struct Hashmap
{
    static const int Ha=999917,maxe=46340;
    int E,lnk[Ha],son[maxe+5],nxt[maxe+5],w[maxe+5];
    int top,stk[maxe+5];
    void clear() {E=0;while (top) lnk[stk[top--]]=0;}
    void Add(int x,int y) {son[++E]=y;nxt[E]=lnk[x];w[E]=MAXINT;lnk[x]=E;}
    bool count(int y)
    {
        int x=y%Ha;
        for (int j=lnk[x];j;j=nxt[j])
            if (y==son[j]) return true;
        return false;
    }
    int& operator [] (int y)
    {
        int x=y%Ha;
        for (int j=lnk[x];j;j=nxt[j])
            if (y==son[j]) return w[j];
        Add(x,y);stk[++top]=x;return w[E];
    }
};
Hashmap f;

int gcd(int a,int b) {if (!b) return a; else return gcd(b,a%b);}
int exgcd(int a,int b,int &x,int &y)
{
    if (!b) {x=1;y=0;return a;}
    int r=exgcd(b,a%b,x,y),t=x;x=y;y=t-a/b*y;
    return r;
}
int exBSGS(int A,int B,int C)
{
    if (C==1) if (!B) return A!=1; else return -1;
    if (B==1) if (A) return 0; else return -1;
    if (A%C==0) if (!B) return 1; else return -1;
    int r,D=1,num=0;
    while ((r=gcd(A,C))>1) //把A,C变成(A,C)=1为止
    {
        if (B%r) return -1;num++;
        B/=r;C/=r;D=((LL)D*A/r)%C; //将多出来的乘给D
    }
    for (int i=0,now=1;i<num;i++,now=((LL)now*A)%C)
        if (now==B) return i; //枚举0~num-1
    int m=ceil(sqrt(C)),Base=1;f.clear();
    for (int i=0;i<=m-1;i++)
    {
        f[Base]=min(f[Base],i);
        Base=((LL)Base*A)%C;
    }
    for (int i=0;i<=m-1;i++)
    {
        int x,y,r=exgcd(D,C,x,y);
        x=((LL)x*B%C+C)%C;
        if (f.count(x)) return i*m+f[x]+num; //别忘了答案加num
        D=((LL)D*Base)%C;
    }
    return -1;
}
int main()
{
    freopen("program.in","r",stdin);
    freopen("program.out","w",stdout);
    while (~scanf("%d%d%d",&A,&C,&B))
    {
        if (!A&&!B&&!C) break;
        int ans=exBSGS(A,B,C);
        if (ans==-1) printf("No Solution\n"); else
        printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值