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

题目

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

在线评测地址:Luogu#4305

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

题目描述

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

输入格式

本题有多组数据。

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

对于每组数据:

第一行一个整数 \(n\)

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

输出格式

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

数据规模及约定

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

分析

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

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

\(60\ \texttt{pts}\)

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

\(100\ \texttt{pts}\)

Solution 1

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

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

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

Solution 2

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

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

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

Code

用了第一种做法,在洛谷少爷机上最慢跑了 \(900\ \textrm{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('\n');
	}

	return 0;
}
posted @ 2020-08-24 17:00  5ab  阅读(137)  评论(0编辑  收藏  举报