有以下两则性质:
1. ne ≡ c ( % N )
2. cd ≡ n ( % N )
现在已知 e , N , c
求 n , d
Data Constraint
对于 30% 的测试数据, N≤106
对于 50% 的测试数据, N≤1014
对于 100% 的测试数据, N≤262
对于 30% 的数据,直接暴力做一下。
对于 50% 的数据,对于有一点数论基础的同学,可以轻易发现 d 就是 e 在 r 下的逆元,这个可以扩展GCD求得。而 r=(p−1)(q−1) ,所以现在问题转化为求 p 和 q 。这个可以直接 N−−√ 枚举求出。算出 d 之后直接快速幂一下。
对于 80%∼90% 的数据,这档分数题目并没有设计。但是我们可以揣摩出题人意图,为了卡掉暴力,出题人应当会有某个 N 的因子比较接近 N−−√ ,使得暴力的复杂度尽可能接近 O(N−−√) 。所以我们调整枚举策略,同时枚举两端,这样可以水80。适当调整枚举策略甚至可以水到90。注意,这只是实际测试,实际上复杂度还是 O(N−−√) 的!但是这种水法在不会正解的情况下往往能发挥出意想不到的效果。
对于 100% 的数据,可以发现 50% 的算法真正的复杂度其实都是枚举求 p,q 。如果我们能快速得出 p,q ,那么问题就解决了。
这里引入一个算法—— Pollard Rho
具体细节,这里不再赘述,详见Pollard_rho.pdf
简述一下它的思想:
假如我们随机一个数,那么它是 N 的因子的概率为 1N√ ,当N很大时,这个概率是很小的,据说比中彩票还低。但如果我们随机两个数 a,b ,当 GCD(abs(a−b),N)>1 时,此时的GCD就是 N 的因子,而且这时的概率会大大提升。其期望复杂度为 O(N14) ,这个我不会证。 PollardRho 就是这样一个随机算法。
其他的细节诸如生成随机数,和判环详见pdf。
温馨提醒:本体做乘法的时候会爆long long,可以用加法代替乘法。
#include
#include
#include
#include
#include
#include
#include
using namespace std ;
typedef long long ll ;
typedef unsigned long long ull ;
ll e , N , c , D ;
ll p , q , r , d ;
void Exgcd( ll a , ll b , ll &x , ll &y ) {
if ( b == 0 ) {
x = 1 ;
y = 0 ;
return ;
}
Exgcd( b , a % b , x , y ) ;
ll t = x ;
x = y ;
y = t - (a / b) * y ;
}
ll Calc() {
ll x , y ;
Exgcd( e , r , x , y ) ;
return (x + r) % r ;
}
ll mul( ll x , ll y ) {
if ( y == 1 ) return x ;
return (2ll * mul( x , y / 2 ) % N + ( y % 2 ? x : 0 )) % N ;
}
ll Power( ll x , ll k ) {
ll s = 1 ;
while ( k ) {
if ( k % 2 == 1 ) s = mul( s , x ) % N ;
x = mul( x , x ) % N ;
k /= 2 ;
}
return s ;
}
ll js( ll x ) { return x < 0 ? -x : x ; }
ll f( ll x ) { return (mul( x , x ) + D) % N ; }
ll gcd( ll x , ll y ) { return y == 0 ? x : gcd( y , x % y ) ; }
int main() {
freopen( "crack.in" , "r" , stdin ) ;
freopen( "crack.out" , "w" , stdout ) ;
srand( time(0) ) ;
cin >> e >> N >> c ;
D = rand() % N ;
while ( 1 ) {
ll a , b ;
a = b = rand() % N + 1 ;
while ( 1 ) {
a = f(a) ;
b = f(f(b)) ;
if ( a == b ) break ;
int d = gcd( js( a - b ) , N ) ;
if ( d == 1 || d == N ) continue ;
if ( d > 1 ) { p = d ; break ; }
}
if ( p ) break ;
D -- ;
}
q = N / p ;
r = N + 1 - (p + q) ;
d = Calc() ;
cout << d << " " << Power( c , d ) << endl ;
return 0 ;
}
以上.