LibreOJ2045

Portal

Description

给出三个正整数(e,N,c(leq2^{62}))。已知(N)能表示成(pcdot q)的形式,其中(p,q)为质数。计算(r=(p-1)(q-1),edequiv 1 pmod r),求(c^d mod 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
",d,pow(c,d));
    return 0;
}
原文地址:https://www.cnblogs.com/VisJiao/p/LOJ2045.html