题解:P9865 [POI 2021/2022 R2] lic
WonderStone_ · · 题解
P9865 [POI 2021/2022 R2] lic
题目大意
对于一个正整数
思路描述
本质上,想要完成题目的要求,求出在
Part 1
因此第一步十分寻常,在
接下来,我们又惊喜地发现:长度
而如果只想找到开头,就不难想到:二分答案。
那么重点就在于:如何写 check 函数。
Part 2
这一部分的写法可能有点抽象,需要用到位运算,如果对这方面的知识有疑问,详情可见这里。
直接讲思路,简单说就是:我们需要对于每个自然数
进一步,要想得到答案,刚才处理好的
那么如何高效地对一个集合进行操作呢?此处提供一个十分具有启发性的方法,有点像状压:这里,此处就只讲一下和本题有关的关键点:
我们可以对
那么,
读取 plans>>i&1就是我们求的。接下来,若取到的值为
而每次枚举
那么具体考虑一下,不难想到,若我们需要计算所有
(实现中可以在计算 __builtin_parity 函数统计
因此,
Part 3
最后,按我们之前设想的,通过__gcd函数从二分的答案开始枚举出 long long。
代码详解
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k,c,t,l,r;
vector<int> p,ans;
bool check(int x){
int cnt=0,now;
//blk同刚才讲的plans
for(int blk=0;blk<(1<<t);blk++){
now=1;
//计算被选中的Pi乘积now
for(int i=0;i<t;i++)
if(blk>>i&1) now*=p[i];
//集合计算
cnt+=(x/now)*(__builtin_parity(blk)?-1:1);
}
//check返回
return cnt>=k;
}
signed main(){
freopen("prime.in","r",stdin);
freopen("prime.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>k>>c;
//预处理质因数P
int now=n;
for(int i=2;i*i<=now;i++)
if(now%i==0){
p.push_back(i);
while(!(now%i)) now/=i;
}
if(now!=1) p.push_back(now);
//二分答案
t=p.size(),l=0,r=1e18;
while(l+1<r){
int mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid;
}
now=r;
//从r枚举出所有答案
while(ans.size()<c){
if(__gcd(now,n)==1) ans.push_back(now);
now++;
}
for(int i=0;i<c;i++) cout<<ans[i]<<" ";
return 0;
}