一道 确实可能很简单的 简单的小题:哈希表。
题目描述
你需要维护一个映射 ,初始 。
有 次操作,每次操作给出二元组 ,表示查询 的值,之后 。
输入格式
第一行,一个正整数 。
接下来 行,每行两个整数 描述一次操作。
输出格式
为了减少输出量,设第 次操作的答案为 ,你只需要输出 对 取模的结果。
既然是模板哈希表,那我就成全他,用 unordered_map 做这道题!
我们只需要开一个 unordered_map,再像正常操作数组那样修改映射就可以了!
#include<bits/stdc++.h>
using namespace std;
char buf[1<<23],*p1=buf,*p2=buf;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
inline unsigned long long rd() {//读入一个 64 位无符号整数
unsigned long long x=0;
char ch=gc();
while(!isdigit(ch))ch=gc();
while(isdigit(ch)) x=x*10+(ch^48),ch=gc();
return x;
}
unordered_map<unsigned long long,unsigned long long> mp;
int main(){
unsigned long long n;
cin>>n;
unsigned long long ans=0;
for(int i=1;i<=n;i++){
unsigned long long x=rd(),y=rd();
ans+=(__int128)mp[x]*i%((__int128)2<<64);
mp[x]=y;
}
cout<<ans;
}
Umm,怎么回事?
哦,unordered_map 底层确实是哈希表,但是他的哈希函数太弱了。
unordered_map 的原理是:
提前开了很多的用于存储的链表,称作桶。对于桶内所有元素,记录 key 和 value,即我们填到 mp 里的这个数(称键值),和 mp 所代表的值。
对于数字 x,
我们会发现有一个问题。
正常来说,unordered_map 是线性时间复杂度(即 ),这是因为基本来说,桶是一个萝卜一个坑,只要找到对应的桶,因为桶内只有一个元素,我们直接就找到对应值了。
可是,邪恶的 _fairytale_ 居然预测了 unordered_map 的哈希函数,并制造了大量哈希函数值相同的数。这使得大量的数都填充到了相同的桶内。对于所有桶内元素,我们都要一个一个找到我们需要的键值,真的是太坏了。
所以,我们一定要让 _fairytale_ 无法猜到我们的哈希函数,所以,我们要使用相对随机的哈希函数,这样他(_fairytale_ 没说代词要用什么)就没有办法了。
于是,我们就换一个更加强大的哈希函数!
//抄袭可耻
#include<bits/stdc++.h>
using namespace std;
char buf[1<<23],*p1=buf,*p2=buf;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
inline unsigned long long rd() {//读入一个 64 位无符号整数
unsigned long long x=0;
char ch=gc();
while(!isdigit(ch))ch=gc();
while(isdigit(ch)) x=x*10+(ch^48),ch=gc();
return x;
}
struct custom_hash {
static uint64_t splitmix64(unsigned long long x) {
x+=0x9e3779b97f4a7c15;
x=(x^(x>>30))*0xbf58476d1ce4e5b9;
x=(x^(x>>27))*0x94d049bb133111eb;
return x^(x>>31);
}
size_t operator()(unsigned long long x) const {
static const unsigned long long FIXED_RANDOM=chrono::steady_clock::now().time_since_epoch().count();
return splitmix64(x+FIXED_RANDOM);
}
};
unordered_map<unsigned long long,unsigned long long,custom_hash> mp;
int main(){
int n;
cin>>n;
unsigned long long ans=0;
for(int i=1;i<=n;i++){
unsigned long long x=rd(),y=rd();
ans+=(__int128)mp[x]*i%((__int128)2<<64);
mp[x]=y;
}
cout<<ans;
}
splitmix64 是一个伪随机数生成算法,也是 java 语言的默认随机数生成算法,这样我们就AC了!
哇,真的AC了!