题意
题解
要求x,需先求e,要求e需要知道p和q的值是多少。
由题可知n=p*q,(p,q为素数),那么易得n的唯一分解就是p和q两个数。
首先我们打一个
1
e
8
{1e8}
1e8以内的素数表,看n的唯一分解的较小的那个质数是否在1e8以内。
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=1e8+10;
int prime[maxn];
ll n=1001733993063167141;
void getprime()
{
for(int i=2;i<=maxn-10;i++)
{
if(!prime[i]) prime[++prime[0]]=i;
for(int j=1;j<=prime[0] && 1ll*i*prime[j]<=maxn-10;j++)
{
prime[i*prime[j]]=1;
if(i%prime[j]==0) break;
}
}
}
int main()
{
getprime();
for(int i=1;i<=prime[0];i++)
{
if(n%prime[i]==0)
{
cout << prime[i] << ' ' << n/prime[i] << endl;
return 0;
}
}
printf("Not found!!\n");
}
发现这个质数真就不在1e8内,emmm,看来只好暴力了。
int main()
{
for(int i=1e8+1;;i+=2)
{
if(n%i==0)
{
cout << i << ' ' << n/i << endl;
return 0;
}
}
printf("Not found!!\n");
}
终于找到了p=891234941,q=1123984201。
(也就跑了5s左右,还行吧。)
找到了d现在我们需要求e。
题目给了一个关键信息:
(
d
∗
e
)
%
(
(
p
−
1
)
(
q
−
1
)
)
=
1
{(d*e)\%((p-1)(q-1))=1 }
(d∗e)%((p−1)(q−1))=1
两边同乘以 1 d % ( ( p − 1 ) ( q − 1 ) ) {\frac{1}{d}\%((p-1)(q-1))} d1%((p−1)(q−1))
⇒ e = 1 d % ( ( p − 1 ) ( q − 1 ) ) {e=\frac{1}{d}\%((p-1)(q-1))} e=d1%((p−1)(q−1))
⇒ e = i n v ( d ) % ( ( p − 1 ) ( q − 1 ) ) {e=inv(d)\%((p-1)(q-1))} e=inv(d)%((p−1)(q−1))
所以当务之急是求出d关于 ( p − 1 ) ( q − 1 ) {(p-1)(q-1)} (p−1)(q−1)取模的逆元。
我们发现(p-1)(q-1)不是质数。。
所以费马小定理不适用这种情况,得通过其它方法求解。
还记得我写的一篇博客专门介绍如何求解逆元
有两种方法求解逆元,两种我都写一下。
注意欧拉函数求完用快速幂时由于mod会很大,稍微一乘就爆long long,所以也需要一个快速乘。
- 欧拉函数
#include<iostream>
#include<algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=1e8+10;
int prime[maxn];
ll multi(ll a,ll b,ll m)
{
ll ans=0;
while(b)
{
if(b&1) ans=(ans+a)%m;
a=(a+a)%m;
b>>=1;
}
return (ans+m)%m;
}
ll qpow(ll a,ll b,ll m)
{
ll ans=1;
while (b) {
if(b&1) ans=multi(ans, a, m);
a=multi(a, a, m);
b>>=1;
}
return ans;
}
ll euler_phi(ll n)
{
ll ans=n;
ll tmp=n;
for(int i=2;i<=sqrt(n);i++)
{
if(tmp%i==0)
{
ans=ans/i*(i-1);
while (tmp%i==0) tmp/=i;
}
}
if(tmp>1) ans=ans/tmp*(tmp-1);
return ans;
}
ll inv(ll a,ll m)
{
return qpow(a, euler_phi(m)-1,m);
}
int main()
{
ll p=891234941,q=1123984201;
ll n,d,c; cin >> n >> d >> c;
ll e=inv(d, (p-1)*(q-1));
cout << e << endl;
}
//1001733993063167141 212353 20190324
我的电脑跑了十几秒。。。
- 拓展欧几里得
#include<iostream>
#include<algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=1e8+10;
int prime[maxn];
void exgcd(ll a,ll b,ll& d,ll& x,ll& y)
{
if(!b) { d=a;x=1;y=0;}
else { exgcd(b, a%b, d, y, x); y-=x*(a/b); }
}
ll inv(ll a,ll m)
{
ll d,x,y;
exgcd(a, m, d, x, y);
return (x+m)%m;
}
int main()
{
ll p=891234941,q=1123984201;
ll n,d,c; cin >> n >> d >> c;
ll e=inv(d, (p-1)*(q-1));
cout << e << endl;
}
//1001733993063167141 212353 20190324
这个就很友好了,200多ms,看来以后这种非质数取模求逆元的用拓展欧几里得算法才是最佳选择。
求出了e,答案信手拈来。
X
=
c
e
%
m
{X=c^e \%m}
X=ce%m
#include<iostream>
#include<algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=1e8+10;
int prime[maxn];
ll multi(ll a,ll b,ll m)
{
ll ans=0;
while(b)
{
if(b&1) ans=(ans+a)%m;
a=(a+a)%m;
b>>=1;
}
return (ans+m)%m;
}
ll qpow(ll a,ll b,ll m)
{
ll ans=1;
while (b) {
if(b&1) ans=multi(ans, a, m);
a=multi(a, a, m);
b>>=1;
}
return ans;
}
void exgcd(ll a,ll b,ll& d,ll& x,ll& y)
{
if(!b) { d=a;x=1;y=0;}
else { exgcd(b, a%b, d, y, x); y-=x*(a/b); }
}
ll inv(ll a,ll m)
{
ll d,x,y;
exgcd(a, m, d, x, y);
return (x+m)%m;
}
int main()
{
ll p=891234941,q=1123984201;
ll n,d,c; cin >> n >> d >> c;
ll e=inv(d, (p-1)*(q-1));
cout << qpow(c,e,n) << endl;
}
//1001733993063167141 212353 20190324
最终答案是:579706994112328949