洛谷P1978 集合 [2017年6月计划 数论08]

时间:2021-08-21 22:09:23

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;
}