JZOJ4458. 【CQOI2016】密钥破解

来源:互联网 发布:java parquet 读写 编辑:程序博客网 时间:2024/05/02 01:42

题目大意

  1. 选取两个不同的质数p , q
  2. N=p×qr=(p1)(q1)
  3. 选取一个小于r的整数e,且er互质
  4. 计算整数d满足 ed  1 ( % r )

有以下两则性质:
1.  ne  c  ( % N )
2.  cd  n  ( % N )
现在已知  e , N , c 
 n , d 

Data Constraint
对于30%的测试数据,N106
对于50%的测试数据,N1014
对于100%的测试数据,N262

题解

对于30%的数据,直接暴力做一下。

对于50%的数据,对于有一点数论基础的同学,可以轻易发现d就是er下的逆元,这个可以扩展GCD求得。而r=(p1)(q1),所以现在问题转化为求pq。这个可以直接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(ab),N)>1 时,此时的GCD就是N的因子,而且这时的概率会大大提升。其期望复杂度为O(N14),这个我不会证。PollardRho就是这样一个随机算法。
其他的细节诸如生成随机数,和判环详见pdf。

温馨提醒:本体做乘法的时候会爆long long,可以用加法代替乘法。

SRC

#include<cstdio>#include<cstdlib>#include<cstring>#include<iostream>#include<algorithm>#include<cmath>#include<ctime>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 ;}

以上.

3 0
原创粉丝点击