【题目】
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,k≤1000
【解题思路】
当我知道题解是爆搜出所有 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)=∏pipi−1,其中 p i p_i pi是 n n n的互不相同的质因子。
那么我们要搜出所有的 n n n,关键在除去所有 p i − 1 p_i-1 pi−1的因子,不妨先筛出所有 1 0 7 10^7 107以内的素数。
然后对于当前搜索的一个 ϕ ( n ) \phi (n) ϕ(n),我们在所有筛出来的 p − 1 p-1 p−1内找有没有 ϕ ( 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
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;
}