CQOI2016 bzoj4522 密钥破解

题意


Description
一种非对称加密算法的密钥生成过程如下:
1.任选两个不同的质数 p,q
2.计算 N=pq,r=(p1)(q1)
3.选取小于 r ,且与 r 互质的整数 e
4.计算整数 d ,使得 ed1(modr)
5.二元组 (N,e) 称为公钥,二元组 (N,d) 称为私钥
当需要加密消息 M 时(假设 M 是一个小于 N 的整数,因为任何格式的消息都可转为整数表示),
使用公钥 (N,e) ,按照 nec(modN) 运算,可得到密文 C
对密文 C 解密时,用私钥 (N,d) ,按照 cdn(modN) 运算,可得到原文 M 。算法正确性证明省略。
由于用公钥加密的密文仅能用对应的私钥解密,而不能用公钥解密,因此称为非对称加密算法。
通常情况下,公钥由消息的接收方公开,而私钥由消息的接收方自己持有。这样任何发送消息的
人都可以用公钥对消息加密,而只有消息的接收方自己能够解密消息。
现在,你的任务是寻找一种可行的方法来破解这种加密算法,即根据公钥破解出私钥,并据此解密密文。
Input
输入文件内容只有一行,为空格分隔的 3 个正整数 e,N,c N262,c<N .
Output
输出文件内容只有一行,为空格分隔的 2 个整数 d,n
Sample Input
3 187 45
Sample Output
107 12
//样例中 p = 11, q = 17


















题解


这个题真的是题意即题解……对 N 素因子分解得 r ,然后算 e r 的逆元 d ,最后 cd 快速幂一下就好了。
关键在于对于 262 级别的数如何快速分解质因子。
这里有一个专门的算法叫做PollardRho,期望时间复杂度是 O(n14) 。主要是用一个有圈的随机数生成器来随机一个数与要分解的数求gcd。具体实现在判圈有所不同,可以采用标准的PollardRho方法 xi=(x2i1+c) ,或者是Floyd判圈算法。这个题保证只有两个素因子,所以可以省去用来判断结尾的MillerRabin。

代码

#include<cstdio>
#include<algorithm>
using namespace std;

typedef long long LL;
LL e,N,c;

inline LL gcd(LL a,LL b)
{   if(!b)return a;
    return gcd(b,a%b);
}
inline LL Abs(LL a){if(a<0)return -a;else return a;}

const int MAXN=10000000LL;
LL Maxn;
LL P[MAXN+5];bool vst[MAXN+5];
void Preload()
{   int i,j;P[0]=0;
    for(i=1;i<=Maxn;i++)vst[i]=0;
    for(i=2;i<=Maxn;i++)
    { if(!vst[i])P[++P[0]]=i;
      for(j=1;j<=P[0];j++)
      { if(i*P[j]>Maxn)break;
        vst[i*P[j]]=1;
        if(i%P[j]==0)break;
      }
    }
    //for(i=1;i<=100;i++)printf("%lld ",P[i]);putchar('\n');
}

LL QM_Normal(LL a,LL b,LL M)
{   LL ret=1;a%=M;
    while(b)
    { if(b&1LL)ret=ret*a%M;
      b>>=1LL;a=a*a%M;
    }
    return ret;
}

LL Multi(LL a,LL b,LL M)
{   LL ret=0;a%=M;
    while(b)
    { if(b&1LL)ret=(ret+a)%M;
      b>>=1LL;a=(a+a)%M;
    }
    return ret;
}
LL QM(LL a,LL b,LL M)
{   LL ret=1;a%=M;
    while(b)
    { if(b&1LL)ret=Multi(ret,a,M);
      b>>=1LL;a=Multi(a,a,M);
    }
    return ret;
}

LL d;
void ExEuclid(LL a,LL b,LL &x,LL &y)
{   if(!b){d=a;x=1;y=0;return;}
    ExEuclid(b,a%b,x,y);
    LL t=x;x=y;y=t-a/b*y;
}
LL Inverse(LL x,LL p)
{   LL inv,t;
    ExEuclid(x,p,inv,t);
    return (inv%p+p)%p;
}

void Solve1()
{   LL r,D,i;
    Maxn=1000;Preload();
    for(i=1;i<=P[0];i++)
     if(N%P[i]==0)break;
    r=(P[i]-1)*(N/P[i]-1);
    D=Inverse(e,r);
    printf("%lld %lld\n",D,QM_Normal(c,D,N));
}

void Solve2()
{   LL r,i,D;
    Maxn=10000000LL;Preload();
    for(i=1;i<=P[0];i++)
     if(N%P[i]==0)break;
    r=(P[i]-1)*(N/P[i]-1);
    D=Inverse(e,r);
    printf("%lld %lld\n",D,QM(c,D,N));
}

LL PollardRho(LL n,LL c)
{   LL i=2,j=1,x,y,tmp;
    x=rand()%(n-1)+1;
    y=(Multi(x,x,n)+c)%n;
    while(1)
    { j++;
      if(x==y)return n;
      tmp=gcd(Abs(y-x),n);
      if(1<tmp&&tmp<n)return tmp;
      if(i==j){i<<=1LL;x=y;}
      y=(Multi(y,y,n)+c)%n;
    }
}
void Solve3()
{   LL r,p,K=10007,D;
    p=N;while(p>=N)p=PollardRho(N,K--);
    r=(p-1)*(N/p-1);
    D=Inverse(e,r);
    //printf("%lld ",r);
    printf("%lld %lld\n",D,QM(c,D,N));
}

int main(){
    freopen("crack.in","r",stdin);
    freopen("crack.out","w",stdout);

    srand(10007);
    scanf("%lld%lld%lld",&e,&N,&c);
    if(N<=1000000)Solve1();
    else if(N<=100000000000000LL)Solve2();
    else Solve3();

    fclose(stdin);fclose(stdout);
    return 0;
}

你可能感兴趣的:(OI-题解-素数分解)