SPOJ MOD - Power Modulo Inverted

题目链接:http://www.spoj.com/problems/MOD/ (POJ 3243双倍经验)

Description

Given 3 positive integers x, y and z, you can find k = xy%z easily, by fast power-modulo algorithm. Now your task is the inverse of this algorithm. Given 3 positive integers x, z and k, find the smallest non-negative integer y, such that k%z = xy%z.
[题目大意]求最小的y使得k ≡ xy (mod z)

Input

About 600 test cases.
Each test case contains one line with 3 integers x, z and k.(1<= x, z, k <=109)
Input terminates by three zeroes. (以三个0结束输入)

Output

For each test case, output one line with the answer, or “No Solution”(without quotes) if such an integer doesn’t exist.

Sample Input

5 58 33
2 4 3
0 0 0

Sample Output

9
No Solution


Solution

此题为扩展BSGS(Baby Steps Giant Steps)模板题

普通BSGS

对于 AxB(modC)
令m= c+1
则原式可变为 Aimj ,其中i,j [1,m]
移项后得 AimBAj(modC)
从1~m枚举j,把 BAjmodC 扔进哈希表里
然后从1~m枚举i,看看 Aim 在不在哈希表里。如果在,则 im - j 即为答案;如果枚举完所有i后仍没找到过,则无解

以上是普通BSGS的流程,只适用于gcd(A,C)=1的情况,这是因为在从①变化至②的过程中,如果Aj在mod C下的逆元不存在,就无法从推出结果的②推回原式①,则求出的“答案”并非真正答案

扩展BSGS

AxB(modC)
由取模定义可得
Ax=kC+B
g=gcd(A,C) ,则有
Axg=kCg+Bg
AxgBg(modCg)
AgAx1Bg(modCg )
如果g不为B的因数的话,显然无解
B=Bg C=Cg ,重复执行以上消掉gcd的步骤,直到 gcd(A,C)=1

这时,原式已变为
DAxcntB(modC)
cnt为执行了消除gcd步骤的次数,D为历次 Ag 之积
现在执行普通的BSGS,将结果加上cnt即为答案

不过,有一点要注意,如果答案在 [0,cnt - 1] 内,BSGS可能会给出错误的答案。此时只需要先暴力扫一遍答案是否可能在 [0,cnt - 1] 内就行了,因为cnt最多只能到log(n),时间完全不虚


Code

这里写图片描述

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define N 100000
#define mod 99991
#define foru(i,l,r) for (int i=l; i<=r; i++)
#define setmem(a,x) memset(a,x,sizeof(a))
typedef long long LL;

int val[N],id[N],stk[N],top;
LL x,y,g;

int gethash(int x) {return x%mod;}
void insert(int x, int y)
{
    int h=gethash(x);
    while (val[h]!=-1&&val[h]!=x) h=(h+1)%mod;
    val[h]=x, id[h]=y;
    stk[++top]=h;
}
int get(int x)
{
    int h=gethash(x);
    while (val[h]!=-1&&val[h]!=x) h=(h+1)%mod;
    if (val[h]==-1) return -1;
    return id[h];
}
void clear()
{
    for (; top; top--) val[stk[top]]=-1;
}
int gcd(int a, int b)
{
    return !b? a: gcd(b,a%b);
}
int bsgs(int A, int B, int C, LL D)
{
    LL m=ceil(sqrt(C)), am=1;
    clear();
    foru(i,1,m)
    {
        am=(am*A)%C;
        insert(am*B%C,i);
    }
    foru(i,1,m)
    {
        D=D*am%C;
        int j=get(D);
        if (j!=-1) return i*m-j;
    }
    return -1;
}
int exbsbg(int a, int b, int c)
{
    LL x=1%c, d=1, g, cnt=0;//c=1的情况要小心
    foru(i,0,100)
    {
        if (x==b) return i;
        x=x*a%c;
    }
    while ((g=gcd(a,c))!=1)
    {
        if (b%g!=0) return -1;
        b/=g, c/=g;
        d=d*(a/g)%c, cnt++;
    }
    int ans=bsgs(a,b,c,d);
    if (ans==-1) return -1;
    else return ans+cnt;
}
int main()
{
    setmem(val,-1);
    int x,z,k;
    while (scanf("%d%d%d",&x,&z,&k)!=EOF&&(x|z|k))
    {
        int ans=exbsbg(x%z,k%z,z);
        if (ans==-1) puts("No Solution");
        else printf("%d\n",ans);
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值