https://codeforc.es/gym/102055/problem/K
给出 n , c n, c n,c, n = p ∗ q n = p*q n=p∗q, p p p和 q q q是x附近相邻的两个质数, c = f 2 30 + 3 m o d n c = f^{2^{30}+3} \ mod\ n c=f230+3 mod n。求出 f f f的值。
RSA解密,令 e = 2 30 + 3 e = {2^{30}+3} e=230+3。令 r = ( p − 1 ) ( q − 1 ) r = (p-1)(q-1) r=(p−1)(q−1)
d ∗ e m o d r ≡ 1 d*e\ mod \ r \equiv 1 d∗e mod r≡1
c = f e m o d n c = f^e \ mod\ n c=fe mod n
f = c d m o d n f = c^d \ mod \ n f=cd mod n
可以暴力在sqrt(n)附近寻找p,q,因为两个素数差距不会很大。
已知e和r,可以解同余方程
e x + r y = 1 ex+ry=1 ex+ry=1
得出d的值。
即可以算出f的值。
此题需要用O(1)快速乘才能通过。
#include
using namespace std;
#define FOR0(a,b) for(int i = a; i < b; ++i)
#define FORE(a,b) for(int i = a; i <= b; ++i)
typedef long long ll;
typedef pair<int,int> pii;
inline void add(ll &a,ll b,ll c){a+=b;if(a>=c)a-=c;}
#define ld long double
inline ll mul(ll a,ll b,ll c){return (a*b-(ll)((ld)a*b/c)*c+c)%c;}
ll exgcd(ll a, ll b, ll& x, ll &y) {
if(b == 0) {
x = 1; y = 0;
return a;
}
ll d = exgcd(b,a%b,y,x);
y -= a/b*x;
return d;
}
inline ll pw(ll x, ll n, ll mod) {
ll ret = 1;
while(n) {
if(n&1) ret = mul(ret,x,mod);
x = mul(x,x,mod);
n >>= 1;
}
return ret;
}
int main() {
int T;
scanf("%d", &T);
int ca = 0;
ll a,b,x,y;
a = (1<<30)+3;
while(T--) {
ll n,c;
scanf("%lld%lld", &n, &c);
ll p = sqrt(n);
while(n%p != 0) p--;
ll q = n/p;
ll r = (p-1)*(q-1);
b = r;
exgcd(a,b,x,y);
x = ((x%b)+b)%b;// 保证是正数
ll f = pw(c,x,n);
printf("Case %d: %lld\n",++ca,f);
}
return 0;
}
d ∗ e m o d r ≡ 1 d*e\ mod \ r \equiv 1 d∗e mod r≡1
本题的d正好与r互质,所以可以算出d的逆元。
e m o d r ≡ d − 1 e\ mod \ r \equiv d^{-1} e mod r≡d−1
对
X = C e m o d n X=C^e\ mod \ n X=Ce mod n
进行欧拉降幂
X = C e m o d r m o d n X=C^{e\ mod \ r} \ mod \ n X=Ce mod r mod n
替换指数。
X = C d − 1 m o d n X=C^{d^{-1}} \ mod \ n X=Cd−1 mod n
即可在2ms之内求出X。需要注意,由于r不是素数,所以不能直接用费马小定理的快速幂求。可以采用扩展欧几里得求d的逆元
#include
using namespace std;
#define FOR0(a,b) for(int i = a; i < b; ++i)
#define FORE(a,b) for(int i = a; i <= b; ++i)
typedef long long ll;
typedef pair<int,int> pii;
#define ld long double
inline ll mul(ll a,ll b,ll c){return (a*b-(ll)((ld)a*b/c)*c+c)%c;}
ll pw(ll x, ll n, ll c) {
ll ret = 1;
while(n) {
if(n&1) ret = mul(ret,x,c);
x = mul(x,x,c);
n >>= 1;
}
return ret;
}
ll exgcd(ll a, ll b, ll& x, ll& y) {
if(b == 0) {
x = 1; y = 0;
return a;
}
ll d = exgcd(b,a%b,y,x);
y -= a/b*x;
return d;
}
ll inv(ll a, ll n) {
ll x,y;
ll d = exgcd(a,n,x,y);
return d == 1 ? (x+n)%n:-1;
}
int main() {
clock_t start,end;
start = clock();
ll n = 1001733993063167141;
ll d = 212353;
ll C = 20190324;
ll p = sqrt(n);
while(n%p != 0) {
p--;
}
ll q = n/p;
// cout << p << " "<< q<
ll phi = (p-1)*(q-1);
// cout << "gcd:" <<__gcd(phi,d) << endl;
ll invd = inv(d,phi);
// cout << invd << endl;
ll X = pw(C,invd,n);
cout << X << endl;
end = clock();
cout<<(double)(end-start)/CLOCKS_PER_SEC*1000<<"ms"<<endl;
return 0;
}