【题解】[JLOI2011]不重复数字

题目

题目来源:CCF 吉林省选 2011;

在线评测地址:Luogu#4305

运行限制:时间 (1.00 extrm{s}),空间 (128 extrm{MiB})

题目描述

给定 (n) 个数,要求把其中重复的去掉,只保留第一次出现的数。

输入格式

本题有多组数据。

第一行一个整数 (T),表示数据组数。

对于每组数据:

第一行一个整数 (n)

第二行 (n) 个数,表示给定的数。

输出格式

对于每组数据,输出一行,为去重后剩下的数,两个数之间用一个空格隔开。

数据规模及约定

  • 对于 (30\%) 的数据,(nle 100),给出的数 (in [0, 100])
  • 对于 (60\%) 的数据,(nle 10^4),给出的数 (in [0, 10^4])
  • 对于 (100\%) 的数据,(1le Tle 50)(1le nle 5 imes 10^4),给出的数在 (32) 位有符号整数范围内。

分析

题意很明确,问题就是怎么解决。我们要实现两个操作,即:

  • 询问一个数是否在集合内;
  • 将一个数插入集合中。

(60 exttt{pts})

我们可以用 set,复杂度是 (mathcal{O}(Tsum (nlog n))),带入极限数据大约是 (50 imes 5 imes 10^4 imes 16=4 imes 10^7),由于平衡树常数大会被卡常而无法通过。

(100 exttt{pts})

Solution 1

这时,我们会想到用哈希表。哈希表的均摊复杂度是 (mathcal{O}(Tsum n)),可以轻松通过这题。

但是一般的哈希表会被卡,又不想写多模数,那么怎么办呢?unordered_set 闪亮登场。

这是 C++11 新增的一个数据结构,对应着一个哈希表。有着官方加持的哈希表跑得飞快,吸氧以后就能过了。

Solution 2

当然,我们还有另一种方法——离散化。

但是,一般的排序算法的极限复杂度就是 (mathcal{O}(nlog n)),依旧会被卡(但是比平衡树好很多)。但是如果用基数排序呢?

单次排序的复杂度就是 (mathcal{O}(nlog_{10} x)),可以比较高效,实际性能待检测。

Code

用了第一种做法,在洛谷少爷机上最慢跑了 (900 extrm{ms}) 左右,很悬。正解应该还是 Hash。

#include <cstdio>
#include <unordered_set>
using namespace std;

unordered_set<int> st;

int main()
{
	int cas, n, tmp;

	scanf("%d", &cas);
	while (cas--)
	{
		st.clear();

		scanf("%d", &n);
		for (int i = 0; i < n; i++)
		{
			scanf("%d", &tmp);

			if (st.find(tmp) == st.end())
			{
				printf("%d ", tmp);
				st.insert(tmp);
			}
		}

		putchar('
');
	}

	return 0;
}

非常感谢您读完此文章。

如果您喜欢这篇文章,请点一个赞支持一下博主。

如果您对此文有意见,欢迎在评论区留下你的看法,5ab 会尽力达到。

如果您真的不喜欢这篇文章,也请不要喷,5ab 欢迎私信反馈意见。

原文地址:https://www.cnblogs.com/5ab-juruo/p/solution-jloi2011_lg4305.html