CF922F Divisibility

题目链接:http://codeforces.com/contest/922/problem/F

题目大意:

  对于一个数集 \(I\),定义 \(f(I)\) 为 \(I\) 中满足条件的数对\((a,b)\)的个数:\(a<b\) 并且 \(a|b\).

  要求构造一个数集 \(I\),数集中元素大于 \(0\) 小于等于 \(n\),并且 \(f(I) = k\).

知识点:  构造

解题思路:

  用一种类似素数筛的方法计算每个数的真因子的个数,顺便把个数累加起来,一旦发现大于等于\(k\)即可停止,我们用这些数来构造即可。当然,如果发现 \(1\) 到 \(n\) 所有的真因子个数加起来都小于 \(k\),直接输出 "\(No\)".

  接下来根据真因子个数从大到小排序,如果找到一个 \(x\) 满足:\(x > m/2\) 并且目前的 \(f( )\) 值减掉这个数真因子数大于或等于 \(k\),就在数集中去除这个数并且更新 \(f()\) 值(因为对于大于 \(m/2\) 的数,它对于答案的贡献便是其真因子数),直到满足 \(f(I) = k\) 即可。(具体证明参考官方题解

AC代码:

 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 typedef long long ll;
 6 typedef pair<ll,int> P;
 7 const int maxn = 3e5+5;
 8 P have[maxn];
 9 bool cut[maxn];
10 bool cmp(const P &a,const P &b){
11     return a.first>b.first;
12 }
13 int main(){
14     int n;
15     ll k,cnt=0;
16     scanf("%d%lld",&n,&k);
17     for(int i=1;i<=n;i++)   have[i].first=0,have[i].second=i;
18     int m;
19     for(m=1;m<=n;m++){
20         for(int j=m*2;j<=n;j+=m) have[j].first++;
21         cnt+=have[m].first;
22 
23         if(cnt>=k)  break;
24     }
25     if(cnt<k)   return 0*printf("No\n");
26     sort(have+1,have+m,cmp);
27     int temp=m;
28     for(int i=1;i<=m;i++){
29         if(have[i].second>m/2&&cnt-have[i].first>=k){
30             cnt-=have[i].first;
31             cut[have[i].second]=true;
32             temp--;
33         }
34         if(cnt==k)  break;
35     }
36     printf("Yes\n");
37     printf("%d\n",temp);
38     for(int i=1;i<=m;i++){
39         if(!cut[i]) printf("%d ",i);
40     }
41     printf("\n");
42     return 0;
43 }

 

posted @ 2018-02-15 21:19  Blogggggg  阅读(495)  评论(0编辑  收藏  举报