codeforce 461DIV2 F题

题意

  题目给出n,k,要求找出一个1到n的子集,(a,b)的对数等于k;(a,b)满足a<b且b%a==0;

分析

  还记不记得求素数的时候的欧拉筛!对就那样!如果把每个数字看作一个点的话,可以通过欧拉筛的方法求入度,然后想一想筛的时候j是如何增加的,可以n/i-1直接求出出度。知道这个结论这个题就不难了。先找到一个范围中,他的边数大于等于k,然后在这个范围内尝试删掉结点是否符合要求。(注意当i>n/2时,在n的范围内就没有出度了,所以可以确定,当在某个范围内边数大于等于k,一点可以通过删除某些结点使边数刚好达到k)。题解的最后那一部分我没有看懂(英语渣智商渣),但是通过这种方法确实可以在cf上A掉这个题;

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 #include <vector>
 6 using namespace std;
 7 const int maxn=300000+100;
 8 int d[maxn];
 9 int n;
10 long long k;
11 int main(){
12     cin>>n>>k;
13     long long sum=0;
14     for(int i=1;i<=n;i++){
15         for(int j=2*i;j<=n;j+=i){
16             d[j]++;
17             sum++;
18         }
19     }
20     if(sum<k){
21             printf("No");
22             return 0;
23     }
24     else
25         printf("Yes\n");
26     long long ps=0;
27     for(int i=1;i<=n;i++){
28         ps+=d[i];
29         if(ps>=k){
30             n=i;
31             break;
32         }
33     }
34     vector<int>ans;
35     for(int i=1;i<=n;i++){
36         int degree=d[i]+(n/i)-1;
37         if(ps-degree<k){
38             ans.push_back(i);
39             continue;
40         }
41         ps-=degree;
42         for(int j=2*i;j<=n;j+=i){
43             if(d[j])d[j]--;
44         }
45     }
46     printf("%d\n",ans.size());
47     for(int i=0;i<ans.size();i++)
48         printf("%d ",ans[i]);
49 return 0;
50 }
View Code

 

posted @ 2018-04-14 00:26  蒟蒻LQL  阅读(145)  评论(0编辑  收藏  举报