P9865 [POI2021~2022R2] lic 题解
bianshiyang · · 题解
题目传送门
题目简意:
给定正整数
分析:
首先我们要知道那些数字是和
接下来考虑筛法,我们可以用
记
此时,我们已经解决了当第
考虑构造函数
现在知道了第
-
是对于每个点都进行二分,得到结果
i ,这只适合每个i 比较稀疏的分布。 -
从第
k 位的x 开始往后找,每次一个数i 判断\gcd(i,n) 是否等于1 ,是的话输出,输出c 位后结束循环,适合每个i 比较稠密的分布。
事实证明,此题解法
解法1
解法2
代码实现
#include<bits/stdc++.h>
#define int long long//记得开long long
using namespace std;
int n,k,c,l,r,meici;
int a[1111],cnt;//a数组存储质因子
bool pd;
void dfs(int x,int y,int dep,int ceshi)//递归求解容斥,x表示循环到第几层,y表示上一轮第几个质数参与运算,dep表示每一项的分母是多少,ceshi表示分子是多少
{
if(x==0)
{
meici+=ceshi/dep;
return;
}
for(int i=y+1;i<=cnt;i++)
{
dfs(x-1,i,dep*a[i],ceshi);
}
}
int judge(int x)//求解res
{
int res=0;
for(int i=1;i<=cnt;i++)
{
meici=0;
dfs(i,0,1,x);
if(i&1) res+=meici;
else res-=meici;
}
res=x-res;
res-=k;
return res;
}
bool pdd(int x)//判断是否合法
{
for(int i=1;i<=cnt;i++)
{
if(x%a[i]==0)
{
return false;
}
}
return true;
}
int work(int x)//朴素二分
{
l=1;
r=1e18;
while(l<=r)
{
int mid=(l+r)>>1;
int op=judge(mid);
if(op>=0) r=mid-1;
else l=mid+1;
}
if(pdd(l)&&judge(l)>=0) return l;//判断取l还是r
else return r;
}
int gcd(int x,int y)
{
if(y==0) return x;
return gcd(y,x%y);
}
signed main()
{
scanf("%lld%lld%lld",&n,&k,&c);
int nn=n;
for(int i=2;i*i<=n;i++)//拆质数
{
if(n%i==0)
{
a[++cnt]=i;
while(n%i==0) n/=i;
}
}
if(n>1) a[++cnt]=n;
int st=work(k);
for(int i=st;;i++)
{
if(gcd(i,nn)==1)
{
printf("%lld ",i);
c--;
}
if(c<=0) break;
}
return 0;
}