其实就是这道题的加强版

把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为质数时,a^{p-1}\equiv 1 (\mod p)

2、当p为质数且一个数x^2\equiv 1(\mod p)时,x%p=1或p-1

如果我们直接用费马小定理来判断质数,会漏判许多卡迈尔克数

所以我们改进一下,把1、2、的条件都用起来

于是就有了二次探测

当我们要判断的数p是一个质数时

我们随机一个数x(当然也可以选定bas)

由1知:x^{p-1}\equiv 1(\mod p)  (这个条件不满足肯定是合数)

如果p-1能被2整除,由2知,我们就可以多一个判断条件:x^{\frac{p-1}{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分解质因数

怎么做呢?

我们把分解质因数想象为猜数游戏

如果我每次只猜一个数(均匀随机)

那么猜中因子的概率为O(\frac{\sigma_0(n)}{n})(当然,你可以求一下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);
	}
}