题目大意
数论题三合一。
第一问求
yz%p
第二问求
yx%p=z
的最小非负整数x
第三问求
Cyz%p
第一问
谁都会做
第二问
大步小步法。
假设解决这样一个问题
ax≡b(modp)
首先考虑a与p互质的情况。那么显然是有循环节的。只需要考虑p以内。
设x=
Ap√−B
aAp√−B≡b(modp)
aAp√≡b∗aB(modp)
A和B都在根号p范围内。
因此可以先枚举B把得到的值扔进哈希表。
然后枚举A在哈希表里检验,注意多个相同取最大的B。
A从1枚举,一开始判掉答案为0即可。
然而这题a与p并不互质。
考虑转化为a与p互质。
如果设
d=(a,p)
a∗ax−1≡b(modp)
ad∗ax−1≡bd(modp)
此时a/d与p/d显然互质,那么有逆元存在。
于是可以继续递归处理,将返回的答案加1。
然而每次减一会不会出现负数?
所以一开始枚举x比较小的情况。
每次p至少除以2,只有log层。
无解判断?
每次b不是d的倍数,或者在最后一层找不到答案。
第三问
具体见组合数取模
#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=500000+10,maxha=1000000,maxw=40;
int ha[maxha+10][2],c[maxha+10];
bool bz[maxha+10];
int pri[maxn],cs[maxn],fac[maxn];
int i,j,k,l,t,n,m,y,z,p,pp,xx,yy,ca,ans,top,tot,cnt,lim;
int qsm(int x,int y,int p){
if (!y) return 1;
int t=qsm(x,y/2,p);
t=(ll)t*t%p;
if (y%2) t=(ll)t*x%p;
return t;
}
int exgcd(int a,int b){
if (!b){
xx=1;yy=0;
return a;
}
int d=exgcd(b,a%b);
int x=yy,y=xx-(a/b)*yy;
xx=x;yy=y;
return d;
}
int getny(int a,int p){
exgcd(a,p);
xx=(xx%p+p)%p;
return xx;
}
int hash(int x){
int k=x%maxha;
while (ha[k][0]!=-1&&ha[k][0]!=x) k=(k+1)%maxha;
return k;
}
int bsgs(int a,int b,int p){
if (b==1) return 0;
int d=exgcd(a,p);
if (d>1){
if (b%d!=0) return -1000000;
b/=d;p/=d;
b=(ll)b*getny(a/d,p)%p;
return bsgs(a,b,p)+1;
}
int i,k,t;
lim=floor(sqrt(p))+1;
fo(i,0,lim-1){
t=(ll)b*qsm(a,i,p)%p;
k=hash(t);
if (!bz[k]){
bz[k]=1;
c[++top]=k;
}
ha[k][0]=t;
ha[k][1]=i;
}
fo(i,1,lim){
t=qsm(a,i*lim,p);
k=hash(t);
if (ha[k][0]==-1) continue;
return i*lim-ha[k][1];
}
return -1000000;
}
void prepare(int p,int pp){
int i,k,t;
fac[0]=1;
fo(i,1,p-1){
k=(i%pp==0)?1:i;
fac[i]=(ll)fac[i-1]*k%p;
}
}
int calc(int n,int p,int pp,int f){
if (n<pp) return fac[n];
int t=1;
t=qsm(fac[p-1],n/p,p);
t=(ll)t*fac[n%p]%p;
//tot+=f*num[n%p];
tot+=f*n/pp;
//tot+=f*num[p]*n/p;
return (ll)t*calc(n/pp,p,pp,f)%p;
}
int C(int n,int m,int p){
cnt=0;
int i,k=p,l,t=floor(sqrt(p));
fo(i,2,t)
if (k%i==0){
pri[++cnt]=i;
cs[cnt]=0;
while (k%i==0){
cs[cnt]++;
k/=i;
}
}
if (k>1){
pri[++cnt]=k;
cs[cnt]=1;
}
l=0;
fo(i,1,cnt){
tot=0;
t=qsm(pri[i],cs[i],1000000007);
prepare(t,pri[i]);
k=calc(n,t,pri[i],1);
k=(ll)k*getny(calc(m,t,pri[i],-1),t)%t;
k=(ll)k*getny(calc(n-m,t,pri[i],-1),t)%t;
k=(ll)k*qsm(pri[i],tot,t)%t;
l=(l+(ll)p/t*getny(p/t,t)%p*k%p)%p;
}
return l;
}
int main(){
//freopen("3283.in","r",stdin);freopen("3283.out","w",stdout);
fo(i,0,maxha) ha[i][0]=-1;
scanf("%d",&ca);
while (ca--){
fo(i,1,top) ha[c[i]][0]=-1,bz[c[i]]=0;
top=0;
scanf("%d%d%d%d",&t,&y,&z,&p);
if (t==1) ans=qsm(y,z,p);
else if (t==2){
fo(i,0,maxw+1)
if (i>maxw||qsm(y,i,p)==z) break;
if (i<=maxw) ans=i;else ans=bsgs(y,z,p);
}
else if (t==3) ans=C(z,y,p);
if (t==2&&ans<0) printf("Math Error\n");/*printf("-1\n");*/
else printf("%d\n",ans);
//printf("%d\n",qsm(y,ans,p));
}
fclose(stdin);fclose(stdout);
}