题意:
已知一个加密公式和其对应的解密公式。
给定三个素数 e ,p,q,已知m=p*q,d存在且唯一。
现在已知加密后的序列,要求加密前的序列。
思路:
数学分析得出一个等式:
要求最小的正整数d,那么可以用扩展欧几里得求的。
然后对每个密文跑超级快速幂就能得到答案
详细的数学推导过程:
因为p,q为素数,那么可知:
可以推出
再由题目给出的公式:
将以第一个式子里的b的同余式代入第二个式子:
那么结合一下:
可以推出:
因为:
所以:
转换一下:
然后就可以直接用扩展欧几里得求解最小的正整数 d 了。
需要注意的是解密的时候由于m太大,直接跑快速幂可能会出现溢出,需要用超级快速幂。
代码:
#include<bits/stdc++.h>
#define ULL unsigned long long
using namespace std;
void ex_gcd(long long a,long long b,long long &x,long long &y){
if(!b){x=1;y=0;}
else {
ex_gcd(b,a%b,y,x);
y -= a/b * x;
}
}
inline ULL fast_mul(ULL x,ULL y,ULL mod){
ULL res = 0;
while(y){
if(y&1){
res += x;
while(res>=mod){
res -= mod;
}
}
y>>=1;
x<<=1;
while(x>=mod){
x -= mod;
}
}return res;
}
inline ULL fast_pow(ULL x,long long y,ULL mod){
ULL res = 1;
while(y){
if(y&1){
res = fast_mul(res,x,mod);
}y>>=1;
x = fast_mul(x,x,mod);
}return res;
}
long long A[105];
int main()
{
int T;
scanf("%d",&T);
while(T--){
long long e , p ,q ;
int n;
scanf("%lld%lld%lld%d",&e,&p,&q,&n);
unsigned long long m = q * p;
unsigned long long lcm = (q-1)*(p-1)/__gcd(q-1,p-1);
long long d , k ;
ex_gcd(e,lcm,d,k);
while(d<0)d+=lcm;
for(int i=0;i<n;i++){
unsigned long long x ;
scanf("%llu",&x);
printf("%llu%c",fast_pow(x,d,m),i<n-1?' ':'\n');
}
}
}