题目链接: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 "); 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 "); 37 printf("%d ",temp); 38 for(int i=1;i<=m;i++){ 39 if(!cut[i]) printf("%d ",i); 40 } 41 printf(" "); 42 return 0; 43 }