洛谷P1978 集合 [2017年6月计划 数论08]
Posted 嘒彼小星
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷P1978 集合 [2017年6月计划 数论08]相关的知识,希望对你有一定的参考价值。
P1978 集合
题目描述
集合是数学中的一个概念,用通俗的话来讲就是:一大堆数在一起就构成了集合。集合有如
下的特性:
•无序性:任一个集合中,每个元素的地位都是相同的,元素之间是无序的。
•互异性:一个集合中,任何两个元素都认为是不相同的,即每个元素只能出现一次。
•确定性:给定一个集合,任给一个元素,该元素或者属于或者不属于该集合,二者必居
其一,不允许有模棱两可的情况出现。
例如 A = {1, 2, 3} 就是一个集合。我们可以知道, 1 属于 A ,即 1 ∈ A ; 4 不属于 A ,
即 4 ∉ A 。一个集合的大小,就是其中元素的个数。
现在定义一个特殊的 k-集合,要求满足:
•集合的所有特性
•对任意一个该集合内的元素 x ,不存在一个数 y ,使得 y = kx 并且 y 属于该集合。即
集合中的任意一个数,它乘以 k 之后的数都不在这个集合内
给你一个由 n 个不同的数组成的集合,请你从这个集合中找出一个最大的 k-集合。
输入输出格式
输入格式:第 1 行:两个整数: n 和 k
第 2 行:n 个整数: a[i] 表示给定的集合
输出格式:第 1 行:一个整数: ans 表示最大的 k-集合的大小
输入输出样例
6 2 2 3 6 5 4 10
3
说明
提示:在样例所给集合中,找出的最大的 2-集合为 {4, 5, 6}
•对于 30% 的数据: n, k ≤ 100
•对于 40% 的数据: a[i] ≤ 231-1
•对于 70% 的数据: n, k ≤ 5000
•对于 100% 的数据: n, k ≤ 105, a[i] ≤ 263-1
Solution
tips:最近做题总是忘记longlong或者空间开小。。。郁闷这道题可以排序后从大到小排序,对于每个数x,若k | x,二分找x / k,标记为不能选
由于是从大往小找,可知当前x是否选取影响不到比它大的数
其实从最小开始选也可以
证明:
对于奇数个连续的k的倍数(即 k * a, k * (a + 1), k * (a + 2)...) 可知从非最大(或最小)开始选一定不优于从最大开始选
对于偶数个连续的k的倍数(即 k * a, k * (a + 1),k * (a + 2),k * (a + 3).。。) 无论从哪一个开始选都等价
因此从最大(或最小)的开始选一定最优
但我们从最大开始,因为找的时候会做除法,不会爆long long范围
从最小开始需要做一些防止爆long long的处理
Code
从小开始找的代码:
#include <bits/stdc++.h> inline void read(long long &x){x = 0;char ch = getchar();while(ch > ‘9‘ || ch < ‘0‘){ch = getchar();}while(ch >= ‘0‘ && ch <= ‘9‘)x = x * 10 + ch - ‘0‘, ch = getchar();} inline long long max(long long a, long long b){return a > b ? a : b;} inline long long min(long long a, long long b){return a > b ? b : a;} inline void swap(long long &a, long long &b){long long tmp = a;a = b;b = tmp;} const long long INF = 0xfffffffffffffff; const int MAXN = 100000 + 10; const int MAXK = 100000 + 10; long long num[MAXN],n,k; bool b[MAXN];//记录哪一些数不能加入,true表示不能加 long long erfen(int l, int r, long long p) { long long mid; while(l < r) { mid = l + ((r - l) >> 1); if(num[mid] >= p)r = mid; else l = mid + 1; } return l; } long long ans; int main() { read(n);read(k); for(int i = 1;i <= n;i ++) { read(num[i]); } std::sort(num + 1, num + 1 + n); float ma = (float)num[n] / (float)k; for(long long i = 1;i <= n;i ++) { if(!b[i]) { ans ++; if(num[i] > ma)continue; long long tmp = erfen(i, n, num[i] * k); if(num[tmp] == num[i] * k) b[tmp] = true; } } printf("%d", ans); return 0; }
从大开始找的代码:
#include <bits/stdc++.h> inline void read(long long &x){x = 0;char ch = getchar();while(ch > ‘9‘ || ch < ‘0‘){ch = getchar();}while(ch >= ‘0‘ && ch <= ‘9‘)x = x * 10 + ch - ‘0‘, ch = getchar();} inline long long max(long long a, long long b){return a > b ? a : b;} inline long long min(long long a, long long b){return a > b ? b : a;} inline void swap(long long &a, long long &b){long long tmp = a;a = b;b = tmp;} const long long INF = 0xfffffffffffffff; const int MAXN = 100000 + 10; const int MAXK = 100000 + 10; long long num[MAXN],n,k; bool b[MAXN];//记录哪一些数不能加入,true表示不能加 long long erfen(int l, int r, long long p) { long long mid; while(l < r) { mid = l + ((r - l) >> 1); if(num[mid] >= p)r = mid; else l = mid + 1; } return l; } long long ans; int main() { read(n);read(k); for(int i = 1;i <= n;i ++) { read(num[i]); } std::sort(num + 1, num + 1 + n); for(long long i = n;i >= 1;i --) { if(!b[i]) { ans ++; if(num[i] % k == 0) { long long tmp = erfen(1, i, num[i] / k); if(num[tmp] * k == num[i]) b[tmp] = true; } } } printf("%d", ans); return 0; }
以上是关于洛谷P1978 集合 [2017年6月计划 数论08]的主要内容,如果未能解决你的问题,请参考以下文章
洛谷P1890 gcd区间 [2017年6月计划 数论09]