解高次同余方程的模板

poj 3243

题目的大意大概就是输入x,z,k(0<=x,z,k<=10^9),输入以0 0 0 结尾,计算满足(x^y)mod z=k,求出满足条件的y.如果找不到满足条件的y,那么直接就输出“No Solusion”(不包含引号).

想着学习也很实在,有模板就直接套模板,我也是这样,没办法,毕竟感觉这些东西有那难懂敲打,只希望自己能够在最短的时间内拿下这些闭嘴

也不知道什么时候能够完完整整的把这些模板学会,完全看懂。。。。

不多说了,直接上模板把,当然出于责任心,小编我生气也会把我已经了解到的给大家解释下微笑,虽然理解的不够深,讲解的不够好,但是也希望能帮到你们吐舌头

#include<iostream>
#include<cmath>

#include<cstdio>
#define MAXX 65535//这个可以设置一个比较大的数,只需要大于等于这个65535都行
using namespace std;
struct hash//构造哈希
{
    int a,b,next;
}Hash[MAXX<<1];
int flag[MAXX+66];
int top,idx;
void ins(int a,int b)//将b插入到哈希表中的a的位置,当然这也是哈希算法的模板,你可以直接记住就行
{
    int k=b&MAXX;
    if(flag[k]!=idx)
    {
        flag[k]=idx;
        Hash[k].next=-1;
        Hash[k].a=a;
        Hash[k].b=b;
        return;
    }
    while(Hash[k].next!=-1)
    {
        if(Hash[k].b==b)return;
        k=Hash[k].next;
    }
    Hash[k].next=++top;
    Hash[top].next=-1;
    Hash[top].a=a;
    Hash[top].b=b;
}
int find(int b)//在哈希表中查询b的所在位置
{
    int k=b&MAXX;
    if(flag[k]!=idx)return -1;//这代表并没有找到对应的b,就直接返回-1
    while(k!=-1)
        {
            if(Hash[k].b==b)return Hash[k].a;
    k=Hash[k].next;
        }
        return -1;
}
int gcd(int a,int b)//直接求最大公约数
{
    return b==0?a:gcd(b,a%b);
}
int ex_gcd(int a,int b,int &x,int &y)//使用扩展欧几里得算法直接计算ax+by=c里边的x和y的值,并且把a和b的最大公约数返回回去,这个也是一个典型的模板,学习数论的一定需要重点掌握
{
    if(!b){x=1;y=0;return a;}
    int d=ex_gcd(b,a%b,y,x);
    y-=a/b*x;
    return d;
}
int inval(int a,int b,int n)找到满足条件的一组解
{
    int x,y,e;
    ex_gcd(a,n,x,y);
    e=(long long)x*b%n;


    return e<0?e+n:e;
}
int pow(long long a,int b,int c)//快速幂算法的模板
{
    long  long ret=1%c;
    a%=c;
    while(b)
    {
        if(b&1)ret=ret*a%c;
        a=a*a%c;
        b>>=1;
    }
    return ret;
}
int Baby(int a,int b,int c)//解决这道题的关键部分,最关键的函数,我来一一说明
{
    top=MAXX;++idx;
    long long buf=1%c,D=buf,k;
    int i,d=0,tem;//前边都只是定义初始化,挺简单的
    for(i=0;i<=100;buf=buf*a%c,++i)//先i从0到100循环验证,如果满足那个条件,直接就返回i

//这里为什么是100呢,因为这个是根据题目给的范围来决定的,题目给出了10^9,也就是说假设X就是1(当然也不是真的1,只是个很接近1的比1大一点的数,因为1的任何次方都是1)那么也就最多需要10*9=90次方才能把那些都试玩,所以这里可以取>=90的任何一个数,不过为了好看点,直接就取了100。

        if(buf==b)return i;
    while((tem=gcd(a,c))!=1)//这个循环主要是为了得到D,就是那个a=b(mod c)的一个解
    {
        if(b%tem)return -1;
        ++d;
        c/=tem;
        b/=tem;
        D=D*a/tem%c;
    }
    int m=(int)ceil(sqrt((double)c));
    for(buf=1%c,i=0;i<=m;buf=buf*a%c,++i)ins(i,buf);//i从0到m循环将(i,a^i%c)插入Hash表中
    for(i=0,k=pow((long long)a,m,c);i<=m;D=D*k%c,++i)
    {
        tem=inval((int)D,b,c);
        int w;
        if(tem>=0&&(w=find(tem))!=-1)
            return i*m+w+d;
    }
    return -1;
}
int main()
{
    int a,b,c;
    while(scanf("%d%d%d",&a,&c,&b)!=EOF,a||b||c)//注意输入顺序,我的a对应的是x,b对应的是k,c对应的是Z
    {
        b%=c;
        int tem=Baby(a,b,c);
        if(tem<0)puts("No Solution");
        else printf("%d\n",tem);
    }
   return 0;
}

还有道基本上一样的题,大家可以看一下poj 2417

就只需要改变一下输入的顺序,为c,a,b就行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值