4522: [Cqoi2016]密钥破解
Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 459 Solved: 223
[ Submit][ Status][ Discuss]
Description
Input
输入文件内容只有一行,为空格分隔的j个正整数e,N,c。N<=2^62,c<N
Output
输出文件内容只有一行,为空格分隔的k个整数d,n。
Sample Input
Sample Output
//样例中 p = 11, q = 17
HINT
Source
由于使用了大量的快速加导致我的程序非常慢 - -(不过容错性强?雾
这里首先讲一讲Pollard-Rho质因数分解算法。这里首先要了解一个叫生日悖论的东西:每23个人中有人生日在同一天的概率大于50%
因此我们可以考虑拓展应用,同理,如果我们在n以内选择一个数p,选择k个数a1,a2……ak,存在|ai-aj|=p的概率为P,当k趋于sqrt(n)级别时,P>1/2
但是遗憾的是,如果我们一组组枚举,时间复杂度没有丝毫改善,并且要耗费内存所无法存储的空间,这时候Pollard-Rho算法就派上用场了,这里我们借助一个神奇的函数f(x)=x^2+a mod n,令xi+1=f(xi),同时考虑求gcd(|xi+1-xi|,n),当其不为1或n时即为n的因子。
但是有的时候会出现环 = = 所以我们可以用Floyd的一个办法:一个a的更新满足a=f(a),而一个b的更新满足b=f(f(b)),显然当a追赶上b时就出现环了,返回寻找失败。
这样计算下去,时间复杂度是均摊的,由于long long会爆炸,我的这一部分的时间复杂度是
的。
然后显然我们将N质因数分解之后,再用快速加在扩欧里到处乱搞即可 = =,时间复杂度
总时间复杂度
#include"bits/stdc++.h"
using namespace std;
typedef long long ll;
static unsigned long long seed;
const unsigned int MOD=2147483659ll;
inline unsigned long long RAND(){
srand(seed); ll re=seed*rand()%MOD+1;
seed=(rand()%MOD*seed+re+seed%233)%MOD+1;
return re*rand()%MOD+1;
}
#define myrand(mod) (RAND()%mod+1)
#define f(x) ((ksc(x,x,n)+1)%n)
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
inline ll ABS(ll x){return x>0?x:-x;}
inline ll ksc(ll a,ll b,ll mo){
a%=mo,b%=mo;ll re=0;
while(a){
if(a%2)re=(re+b)%mo;
b=(b+b)%mo;a>>=1;
}
return re;
}
inline ll find(ll n,ll x0){
ll a=f(x0),b=f(f(x0)),p;
while(a!=b){
p=gcd(ABS(a-b),n);
if(p>1)return p==n?-1:p;
a=f(a);b=f(f(b));
}
return -1;
}
inline ll Pollard_Rho(ll n){
ll p=-1;
for(;p==-1;p=find(n,myrand(n-1)));
return p;
}
ll ksm(ll a,ll b,ll mo){
ll re=1;
while(b){
if(b%2)re=ksc(re,a,mo);
a=ksc(a,a,mo);b>>=1;
}
return re;
}
void exgcd(ll a,ll b,ll&x,ll&y,ll mo){
if(b==0){x=1,y=0;return;}
exgcd(b,a%b,y,x,mo);
y=(y-ksc(a/b,x,mo)+mo)%mo;
}
ll e,N,c,d,n,r;
int main(){
cin>>e>>N>>c;ll p=Pollard_Rho(N),y;
r=(p-1)*(N/p-1);exgcd(e,r,d,y,r);
n=ksm(c,d,N);printf("%lld %lld\n",d,n);
return 0;
}