信息学奥赛一本通 1463:门票

【题目链接】

ybt 1463:门票

【题目考点】

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;
}
相关推荐
复杂网络2 小时前
多个 Claude Code 与多个 Codex 协同工作:设计与实现方案
算法
apocelipes16 小时前
常用编程语言和库的正则表达式性能对比
c语言·c++·python·性能优化·golang·开发工具和环境
HjhIron18 小时前
面试常客:字符串算法从入门到进阶
算法·面试
吴佳浩19 小时前
DeepSeek DSpark:Confidence-Scheduled Speculative Decoding 技术解析
人工智能·算法·deepseek
触底反弹20 小时前
🧠 搞懂 Token,才算真正入门大模型——从分词原理到 Embedding 语义实战
javascript·人工智能·算法
vivo互联网技术1 天前
ICLR 2026 | 基于后验采样的图像恢复方法LearnIR:人脸去阴影、去雾
人工智能·算法·aigc
浮生望1 天前
JS字符串与回文算法:从包装类到双指针的面试进阶之路
javascript·算法
黄敬峰1 天前
面试必刷:从JS底层包装类到双指针,彻底搞懂字符串与回文算法
算法
地平线开发者2 天前
J6B vio scenario sample
算法