首页 > 代码库 > 洛谷P1978 集合 [2017年6月计划 数论08]

洛谷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-集合的大小

输入输出样例

输入样例#1:
6 2	2 3 6 5 4 10
输出样例#1:
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]