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 }