【题目链接】
【题目考点】
1. 哈希表
相关知识见:【模板:哈希表】信息学奥赛一本通 1456:【例题2】图书管理
【解题思路】
解法1:链地址法实现哈希表
数据范围限制为 65536 K B 65536KB 65536KB。
哈希表中最多可能保存 2 ∗ 10 6 2*10^6 2∗106个元素,平均每个元素占用内存 65536 ∗ 1024 / ( 2 ∗ 10 6 ) ≈ 33 B 65536*1024/(2*10^6)\approx 33B 65536∗1024/(2∗106)≈33B。
使用STL中的unordered_set类,内存开销比较大,当存储元素个数达到 2 ∗ 10 6 2*10^6 2∗106时,容易发生内存超限。
因此本题必须手动实现 哈希表。
开放地址法对内存空间需求较大,需要保证负载因子较小(一般为0.7)才可以降低哈希冲突的概率,因此表长会比存储的元素数量更大,有很多空间不能保存数据,对空间需求较高。
而链地址法的负载因子可以大于1,对内存空间需求相对较小,较为灵活。
参考【模板:哈希表】信息学奥赛一本通 1456:【例题2】图书管理中解法2可以以链地址法实现哈希表。
可以选择将哈希表写成类,或只是声明全局数组和函数来实现。
本题给定了初值 a 0 = 1 a_0=1 a0=1,以及递推公式 ( A ⋅ a i + a i m o d B ) m o d C (A\cdot a_i+a_i\bmod B)\bmod C (A⋅ai+aimodB)modC,想要求出第一次出现重复项的编号。即当求出的值为 a i a_i ai时,如果 a i a_i ai在先前已经出现过,就输出 i i i。
如果答案超过 2 ∗ 10 6 2*10^6 2∗106,就输出-1。
我们可以根据 a a a序列的初始值和递推式,依次递推求出 a a a序列的每一项,设其中的一项为 d d d。注意 A ⋅ a i A\cdot a_i A⋅ai这一步要在long long类型下进行计算。
当求出 a i = d a_i=d ai=d时,首先在哈希表中查找是否存在 d d d,
- 如果哈希表中存在 d d d,则输出 i i i,结束程序。
- 如果哈希表中不存在 d d d,则将 d d d插入哈希表。
循环次数为 2 ∗ 10 6 2*10^6 2∗106,如果跳出了循环,则输出-1。
【题解代码】
解法1:链地址法实现哈希表
- 写法1:写全局数组和函数实现哈希表,链表中结点地址为int类型
cpp
#include <iostream>
#include <algorithm>
using namespace std;
#define N 2000003
struct Node
{
int val;
int next;
} node[N];
int p, data[N];//data[i]:哈希值为i的单链表的第一个结点的地址
int Hash(int key)
{
return key%N;
}
void insert(int key)
{
int h = Hash(key), np = ++p;
node[np].val = key;
node[np].next = data[h];
data[h] = np;
}
int count(int key)
{
int h = Hash(key);
for(int i = data[h]; i != 0; i = node[i].next)
if(node[i].val == key)
return 1;
return 0;
}
int main()
{
int a, b, c, d = 1;
cin >> a >> b >> c;
insert(d);
for(int i = 1; i <= 2000000; ++i)
{
d = ((long long)a*d+d%b)%c;
if(count(d) == 1)
{
cout << i;
return 0;
}
else
insert(d);
}
cout << -1;
return 0;
}
- 写法2:实现HashSet类,链表中结点地址为Node*类型
cpp
#include <bits/stdc++.h>
using namespace std;
#define N 2000003
struct Hash
{
unsigned operator () (int key)
{
return key%N;
}
};
template<class T, class HashFunc>
struct HashSet//开散列
{
struct Node
{
T key;
Node *next = nullptr;
} node[N], *p = node, *data[N] = {};
HashFunc hash;
void insert(T key)
{//头插法
if(count(key) == 1)//如果哈希表中已经存在key,则不插入
return;
int h = hash(key);
Node *np = ++p;
np->key = key;;
np->next = data[h];
data[h] = np;
}
int count(T key)//获取关键字key的个数
{
int h = hash(key);
for(Node *i = data[h]; i != nullptr; i = i->next)
if(i->key == key)
return 1;
return 0;
}
};
HashSet<int, Hash> hs;
int main()
{
int a, b, c, d = 1;
cin >> a >> b >> c;
hs.insert(d);
for(int i = 1; i <= 2000000; ++i)
{
d = ((long long)a*d+d%b)%c;
if(hs.count(d) == 1)
{
cout << i;
return 0;
}
else
hs.insert(d);
}
cout << -1;
return 0;
}