其实就是这道题的加强版
把10^9改为10^16
首先我们要会O(1)快速乘
inline LL mul(LL x,LL y,LL mod)
{
LL ret=x*y-(LL)((long double)x*y/mod+0.5)*mod;
return ret<0?ret+mod:ret;
}
然后要会miller_robin判断质数
怎么推导miller_robin呢?
我们先有了几个条件
1、费马小定理:当p为质数时,
2、当p为质数且一个数时,x%p=1或p-1
如果我们直接用费马小定理来判断质数,会漏判许多卡迈尔克数
所以我们改进一下,把1、2、的条件都用起来
于是就有了二次探测
当我们要判断的数p是一个质数时
我们随机一个数x(当然也可以选定bas)
由1知: (这个条件不满足肯定是合数)
如果p-1能被2整除,由2知,我们就可以多一个判断条件:%p =1或p-1 (当这个不满足时p也是合数)
如果不能整除就不行了。。。
由于直接从p-1到(p-1)/2需要重新求快速幂
所以我们先把p-1提出所有的2
设p-1=r*2^t
于是我们就可以从低次向高次做了,这样就不用求许多次快速幂,每次用一下乘法就可以了
代码:ksm是快速幂
inline bool mler(LL n)
{
if(n==1)return 0;
LL r=n-1,t=0;
while(!(r&1))t++,r>>=1;
for(int i=0;i<6;i++){
if(bas[i]==n)return 1;
if(n%bas[i]==0)return 0;
LL now=0,pre=ksm(1ll*bas[i],r,n);
for(int k=0;k<t;swap(now,pre),k++)
if((now=mul(pre,pre,n))==1&&pre!=1&&pre!=n-1)
return 0;
if(pre!=1)return 0;
}
return 1;
}
我们还要学会Pollard_Rho分解质因数
怎么做呢?
我们把分解质因数想象为猜数游戏
如果我每次只猜一个数(均匀随机)
那么猜中因子的概率为(当然,你可以求一下gcd,这样的猜中几率会更高)
但是我们可以通过一些方法让它的猜测具有趋向性(让它不是均匀随机,这样可能会更容易猜中)
我们可以均匀随机两个数,用它们的差的绝对值来作为猜测的数
这样它猜数就不是均匀随机的了,有可能猜中的概率会变大
如何随机这两个数?
PR的方法就是:y=(x*x+c)%(n-1)+1,多次随机每次把上一次的y作为当前的x。c是常数
PR之所以高效,是因为它的随机数的性质特别好,可以较大概率的猜中因子
但是每次求gcd会很慢,于是我们可以累积127次,来求一次gcd
代码:
inline LL ab(LL x){return x<0?-x:x;}
LL rho(LL n,LL c)
{
LL d=1,i=1,j=0,x=rand()%(n-1)+1,y=x,tmp=1;
while(d==1){
x=(mul(x,x,n)+c)%n;
tmp=mul(tmp,ab(x-y),n);
if(++j==i)i<<=1,y=x,d=gcd(n,tmp);
if(!(j&127))d=gcd(n,tmp);
}
return d==n?rho(n,c+1):d;
}
void plar(LL n)
{
if(mler(n)){pr[++pcnt]=n;return;}
LL d=rho(n,3);
plar(d);plar(n/d);
}
这道题其实不难想,但是因为忘了PR,所以就只写了50pts。。。
AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
using namespace std;
#define LL long long
int bas[6]={2,3,7,11,37,19260817};
LL pr[1005],pcnt;
LL a[105],p[105],acnt;
LL zong[10000005];int cnt;
map<LL,bool> vis;
vector<pair<LL,LL> > ans;
inline LL mul(LL x,LL y,LL mod)
{
LL ret=x*y-(LL)((long double)x*y/mod+0.5)*mod;
return ret<0?ret+mod:ret;
}
inline LL ksm(LL x,LL y,LL mod)
{
LL ret=1;
while(y){
if(y&1)ret=mul(ret,x,mod);
y>>=1;x=mul(x,x,mod);
}
return ret;
}
inline LL gcd(LL x,LL y){return !y?x:gcd(y,x%y);}
inline bool mler(LL n)
{
if(n==1)return 0;
LL r=n-1,t=0;
while(!(r&1))t++,r>>=1;
for(int i=0;i<6;i++){
if(bas[i]==n)return 1;
if(n%bas[i]==0)return 0;
LL now=0,pre=ksm(1ll*bas[i],r,n);
for(int k=0;k<t;swap(now,pre),k++)
if((now=mul(pre,pre,n))==1&&pre!=1&&pre!=n-1)
return 0;
if(pre!=1)return 0;
}
return 1;
}
inline LL ab(LL x){return x<0?-x:x;}
LL rho(LL n,LL c)
{
LL d=1,i=1,j=0,x=rand()%(n-1)+1,y=x,tmp=1;
while(d==1){
x=(mul(x,x,n)+c)%n;
tmp=mul(tmp,ab(x-y),n);
if(++j==i)i<<=1,y=x,d=gcd(n,tmp);
if(!(j&1023))d=gcd(n,tmp);
}
return d==n?rho(n,c+1):d;
}
void plar(LL n)
{
if(mler(n)){pr[++pcnt]=n;return;}
LL d=rho(n,3);
plar(d);plar(n/d);
}
LL n;
void dfs(int i,LL sum)
{
if(vis.count(sum))return;
LL tmp=sum;
if(i==acnt+1){
if(sum<n)zong[++cnt]=sum;
return;
}
for(int j=0;j<=p[i];j++){
dfs(i+1,sum);
sum/=a[i];
}
vis[tmp]=1;
}
int main()
{
int T,i,j;LL mi;
scanf("%d",&T);
while(T--){
scanf("%lld",&n);mi=n;
vis.clear();ans.clear();cnt=0;vis[1]=1;
for(i=1;i<=35;i++){
acnt=0;pcnt=0;
plar(n*i+1);
sort(pr+1,pr+pcnt+1);
for(j=1;j<=pcnt;j++){
if(j==1||pr[j]!=pr[j-1])a[++acnt]=pr[j],p[acnt]=1;
else p[acnt]++;
}
dfs(1,n*i+1);
}
sort(zong+1,zong+cnt+1);
for(i=1;i<=cnt;i++){
LL inv=ksm(zong[i],n-2,n);
if(inv<mi){
mi=inv;
ans.push_back(make_pair(zong[i],inv));
}
}
printf("%d\n",ans.size());
for(i=0;i<int(ans.size());i++)
printf("%lld %lld\n",ans[i].first,ans[i].second);
}
}