2018 CCPC-final K - Mr. Panda and Kakin(RSA解密) 和 第十届蓝桥省赛C++A组填空题 RSA解密(非暴力解法)

https://codeforc.es/gym/102055/problem/K

K - Mr. Panda and Kakin

题意

给出 n , c n, c n,c n = p ∗ q n = p*q n=pq 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=(p1)(q1)
d ∗ e   m o d   r ≡ 1 d*e\ mod \ r \equiv 1 de mod r1
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;
}

蓝桥省赛 RSA解密

2018 CCPC-final K - Mr. Panda and Kakin(RSA解密) 和 第十届蓝桥省赛C++A组填空题 RSA解密(非暴力解法)_第1张图片

题解

d ∗ e   m o d   r ≡ 1 d*e\ mod \ r \equiv 1 de mod r1
本题的d正好与r互质,所以可以算出d的逆元。
e   m o d   r ≡ d − 1 e\ mod \ r \equiv d^{-1} e mod rd1

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=Cd1 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;
}

你可能感兴趣的:(数论,RSA)