Description
有三问,给出y,z,p,求
1:yzmodp
2:xy≡z(modp)中最小的非负整数x
3:yx≡z(modp)中最小的非负整数x
2,3问若无解输出Orz, I cannot find x!
y,z,p<=10^9,且p为质数
数据组数<=10,保证每一组数据都是同一问。
Solution
第一问不会的还是不要看下去了。。直接去百度快速幂。。
第二问扩展欧几里得不解释。。不会的右转百度。。
第三问貌似没有什么好做法,不过由于费马小定理的限制,答案在0~p-2之间。
那么有没有什么快速的枚举算法呢?
二分很明显直接躺,
分块?!
似乎可以,预处理处y^i(0<=i<=m,m为块的大小)
然后枚举答案存在于k这个块中,那么相对应的z乘上y^-km
因为p为质数,所以直接逆元就好了。
Code
#include<map>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
int bz,ty,sz;
ll n,m,ans,p,z,ni;
map<ll,ll> h;
bool pd;
ll exgcd(ll a,ll b,ll &x,ll &y) {
if (!b) {x=1;y=0;return a;}
ll xx,yy;ll gcd=exgcd(b,a%b,xx,yy);
x=yy;y=xx-a/b*yy;return gcd;
}
ll mi(ll x,ll y) {
ll z=x%=p;
for(y--;y;y/=2,(x*=x)%=p) if (y&1) (z*=x)%=p;
return z;
}
int main() {
for(scanf("%d%d",&ty,&bz);ty;ty--) {
scanf("%lld%lld%d",&n,&m,&p);
if (bz==1) {
ans=n%=p;
for(m--;m;m/=2,(n*=n)%=p) if (m&1) (ans*=n)%=p;
printf("%lld\n",ans);
} else if (bz==2) {
ll gcd=exgcd(n,p,ans,z);
if (m%gcd) {printf("Orz, I cannot find x!\n");continue;}
((ans%=p)+=p)%=p;(ans*=m)%=p;
printf("%lld\n",ans);
} else {
if (!(n%p)) {printf("Orz, I cannot find x!\n");continue;}
h.clear();z=1;sz=int(sqrt(p));
fo(i,0,sz) h[z]=i+1,(z*=n)%=p;
ni=mi(n,(p-2)*sz);pd=0;m%=p;
fo(i,0,p/sz) {
if (h[m]) {pd=1;printf("%lld\n",(ll)i*sz+h[m]-1);break;}
(m*=ni)%=p;
}
if (!pd) printf("Orz, I cannot find x!\n");
}
}
}
Ps:渣代码跑的慢。。。