Codeforces 922.F Divisibility
Imp is really pleased that you helped him. But it you solve the last problem, his gladness would raise even more.
- a is strictly less than b;
- a divides b without a remainder.
You are to find such a set , which is a subset of {1, 2, ..., n} (the set that contains all positive integers not greater than n), that .
The only line contains two integers n and k .
If there is no answer, print "No".
Otherwise, in the first line print "Yes", in the second — an integer m that denotes the size of the set you have found, in the second line print m integers — the elements of the set , in any order.
If there are multiple answers, print any of them.
3 3
No
6 6
Yes
5
1 2 4 5 6
8 3
Yes
4
2 4 5 8
In the second sample, the valid pairs in the output set are (1, 2), (1, 4), (1, 5), (1, 6), (2, 4), (2, 6). Thus, .
In the third example, the valid pairs in the output set are (2, 4), (4, 8), (2, 8). Thus, .
题目大意:在1~n中选任意个数组成一个集合I,定义f(I) = I中的每个数被I中的其它的多少个数整除的和.已知f(I) = k,求I.
分析:全程凭感觉做的一道题......
令d(i)表示i被1~i-1这些数整除的数的个数,e(i) = Σd(j) (1 ≤ j ≤ i).首先需要猜出一个结论:当e(n) ≥ k时,是肯定有解的. 更近一步,当e(i) ≥ k时,肯定有解,那么就可以把>i的数给丢掉.
假设e(pos) ≥ k,k变成e(pos) - k,将pos / 2 + 1到pos的d全都加入优先队列中,每次弹出最大的d,如果k≥d,则k -= d,并丢掉这个d对应的i.这是基本做法,为什么只需要pos / 2 + 1到pos的数就可以了呢?
如果考虑的数≤pos / 2,那么删掉这个数的贡献就不只是d,因为[pos / 2 + 1,pos]中有数是它的倍数,这个不好考虑.那为什么只考虑pos / 2 + 1到pos的数就一定最后能让k变成0呢?整除数m的数的个数是O(m ^ (1/3))的.而>m/2并且<m的质数的个数大约是个,一般后者的数量都比前者大,而质数的贡献是1,所以只删去质数就能满足要求,有极少数的数会出现后者比前者小,由于差的非常小,按照上述方法贪心地删就好了.
如果不按照d来考虑贡献,可以考虑只删除1~pos的质数,对于质数i,它的贡献是[pos / i],删除当前质数不影响其他质数的贡献,其实和上面的贪心方法差不多.
我曾经考虑过正向构造,每次考虑添加哪个数进去,但是贡献不好算,而且想不到什么好的策略. 这个方法就是把可能的数摆在你的面前,你要在里面删数,不仅要考虑能否满足要求,并且还要考虑贡献的计算问题. 挺考验数学直觉的.
#include <cstdio> #include <queue> #include <cstring> #include <iostream> #include <algorithm> using namespace std; int n,k,sum[300010],d[300010],cur,leftt,vis[300010],ans; priority_queue <pair<int,int> >q; int main() { scanf("%d%d",&n,&k); for (int i = 1; i <= n; i++) for (int j = i * 2; j <= n; j += i) d[j]++; for (int i = 1; i <= n; i++) { sum[i] = sum[i - 1] + d[i]; if (sum[i] >= k) { leftt = sum[i] - k; cur = i; break; } } if (!cur) puts("No"); else { puts("Yes"); if (leftt == 0) { printf("%d\n",cur); for (int i = 1; i <= cur; i++) printf("%d ",i); } else { for (int i = cur / 2 + 1; i <= cur; i++) q.push(make_pair(d[i],i)); while (leftt) { pair <int,int> temp = q.top(); q.pop(); if (leftt >= temp.first) { leftt -= temp.first; vis[temp.second] = 1; } } for (int i = 1; i <= cur; i++) if (!vis[i]) ans++; printf("%d\n",ans); for (int i = 1; i <= cur; i++) if (!vis[i]) printf("%d ",i); } } return 0; }