【搜索+素性测试】BZOJ4803 逆欧拉函数

【题目】
BZOJ
给定 ϕ ( n ) \phi (n) ϕ(n),求第 k k k小的 n n n
ϕ ( n ) ≤ 1 0 14 , k ≤ 1000 \phi (n) \leq 10^{14},k\leq 1000 ϕ(n)1014,k1000

【解题思路】
当我知道题解是爆搜出所有 n n n的时候惊呆了,什么鬼可行的 n n n只有不到 5 × 1 0 5 5\times 10^5 5×105个???

知道做法写思路系列。

首先我们都知道 ϕ ( n ) = ∏ p i − 1 p i \phi (n)=\prod \frac {p_i-1} {p_i} ϕ(n)=pipi1,其中 p i p_i pi n n n的互不相同的质因子。

那么我们要搜出所有的 n n n,关键在除去所有 p i − 1 p_i-1 pi1的因子,不妨先筛出所有 1 0 7 10^7 107以内的素数。

然后对于当前搜索的一个 ϕ ( n ) \phi (n) ϕ(n),我们在所有筛出来的 p − 1 p-1 p1内找有没有 ϕ ( n ) \phi (n) ϕ(n)的约数,若有的话,那么求的 n n n里面一定有 p p p这个因子(注意可能有多个)。

ϕ ( n ) + 1 \phi(n) +1 ϕ(n)+1是一个素数,说明我们可以得到一个新的 n n n,当 ϕ ( n ) + 1 > 1 0 7 \phi(n)+1>10^7 ϕ(n)+1>107,需要使用 miller-rabin \text{miller-rabin} miller-rabin
然后就没有了,复杂度玄学。

一些小细节是,由于每个 p p p的因子可能在 n n n中有多个,我们应该从大到小枚举质因子。

【参考代码】

#include<bits/stdc++.h>
using namespace std;

typedef long double db;
typedef long long ll;
const int N=1e7+5;
const db eps=0.5;
int cnt,K,pnum,pri[N],bo[N];
ll n,ans[N];

namespace MR
{
	ll mul(ll x,ll y,ll mod){return ((x*y-(ll)((db)x/mod*y+eps)*mod)%mod+mod)%mod;}
	ll qpow(ll x,ll y,ll mod)
	{
		ll res=1;
		for(;y;y>>=1,x=mul(x,x,mod))if(y&1)res=mul(res,x,mod);
		return res;
	}
	bool checkpri(ll a,ll y,int cnt,ll mod)
	{
		ll x=qpow(a,y,mod);if(x==1 || x==mod-1) return 1;
		for(int i=1;i<=cnt;++i)
		{
			x=mul(x,x,mod);
			if(x==1) return 0;
			if(x==mod-1) return 1;
		}
		return 0;
	}
	bool miller_rabin(ll x)
	{
		if(x==2) return 1;
		if(x%2==0) return 0;
		ll y=x-1;int cnt=0;
		while(y%2==0) y>>=1,++cnt;
		for(int i=1;i<=5;++i) if(!checkpri(rand()%(x-2)+1,y,cnt,x)) return 0;
		return 1;
	}
}
using namespace MR;

void sieve()
{
	for(int i=2;i<N;++i) 
	{
		if(!bo[i]) pri[++pnum]=i;
		for(int j=1;1ll*pri[j]*i<N && j<=pnum;++j)
		{
			bo[pri[j]*i]=1;
			if(!(i%pri[j])) break;
		}
	}
}

void dfs(ll phi,ll n,int fr)
{
	if(phi+1>N && miller_rabin(phi+1)) ans[++cnt]=n*(phi+1);
	for(int i=fr;i;--i)
	{
		if(!(phi%(pri[i]-1)))
		{
			ll t1=phi/(pri[i]-1),t2=n,t3=1;
			while(!(t1%t3)){t2*=pri[i];dfs(t1/t3,t2,i-1);t3*=pri[i];}
		}
	}
	if(phi==1) ans[++cnt]=n;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("BZOJ4803.in","r",stdin);
	freopen("BZOJ4803.out","w",stdout);
#endif
	srand(19233333);
	scanf("%lld%d",&n,&K);
	sieve();dfs(n,1,pnum);
	sort(ans+1,ans+cnt+1);
	for(int i=1;i<=K;++i) printf("%lld ",ans[i]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值