LibreOJ2045 - 「CQOI2016」密钥破解

Portal

Description

给出三个正整数\(e,N,c(\leq2^{62})\)。已知\(N\)能表示成\(p\cdot q\)的形式,其中\(p,q\)为质数。计算\(r=(p-1)(q-1),ed\equiv 1 \pmod r\),求\(c^d \bmod N\)

Solution

其实主要就是一件事:分解大数\(N\)。这里要用到一个叫做Pollard's Rho的算法,可以在约\(O(n^{\frac{1}{4}})\)的时间复杂度上求出一个\(n\)的因数。具体原理是生日悖论,但我不知道为什么这个是悖论...伪代码如下:

nxt(x)
    return (x*x+step) mod n
PR(n)
    while true
        step=rand()
        a=b=rand()
        while true
            a=nxt(a),b=nxt(nxt(b))
            if(a==b) break
            g=gcd(abs(a-b),n)
            if(g!=1) return g

由于\(nxt(x)\)是循环的,所以用以\(a\)的两倍速度前进的\(b\)进行判断,一旦\(a=b\)则说明出现环,尝试换一个参数\(step\)去随机。

Code

//「CQOI2016」密钥破解
#include <bits/stdc++.h>
typedef long long lint;
lint n;
int pr[10]={0,2,3,5,7,11,13,17};
lint multi(lint a,lint b){lint t=a*b-(lint)((long double)a*b/n+1e-8)*n;if(t<0)return t+n;return t;}
inline lint abs(lint x) {return x<0?-x:x;}
inline lint gcd(lint x,lint y) {return y?gcd(y,x%y):x;}
int step;
inline lint nxt(lint x) {return (multi(x,x)+step)%n;}
lint PR(lint n)
{
    while(true)
    {
        step=rand();
        lint a,b; a=b=rand();
        while(true)
        {
            a=nxt(a),b=nxt(nxt(b));
            if(a==b) break;
            lint g=gcd(abs(a-b),n);
            if(g!=1) return g;
        }
    }
}
inline void exgcd(lint a,lint &x,lint b,lint &y)
{
    if(b==0) {x=1,y=0; return;}
    lint x1,y1; exgcd(b,x1,a%b,y1);
    x=y1,y=x1-a/b*y1;
}
lint inv(lint a,lint m)
{
    lint x,y; exgcd(a,x,m,y);
    while(x<0) x+=m;
    return x;
}
lint pow(lint x,lint y)
{
    lint r=1,t=x;
    for(lint i=y;i;i>>=1,t=multi(t,t)) if(i&1) r=multi(r,t);
    return r;
}
int main()
{
    lint e,c;
    scanf("%lld%lld%lld",&e,&n,&c);
    lint p=0,q=0;
    for(int i=1;i<=7;i++) {if(n%pr[i]==0) p=pr[i],q=n/p; break;}
    if(!p) p=PR(n),q=n/p;
    lint d=inv(e,(p-1)*(q-1));
    printf("%lld %lld\n",d,pow(c,d));
    return 0;
}
posted @ 2018-05-26 12:44  VisJiao  阅读(236)  评论(0编辑  收藏  举报