[JLOI2011]不重复数字
又是无聊写题解系列
题目传送门
刚学hash,个人觉得hash挺玄学的。
sol
题目意思清晰,\(t\)组数据,每一次给定n个数字,把只出现过一次的数字输出
刚看题,以为挺水的,暴力用逻辑数组存起来,\(O(n^2)\),可是数据范围为
\(1≤T≤50\) ,\(1≤n≤5×10^4\),一看就过不了嘛,所以我们要写hash表或者unordered_map。
关于unordered_map:
在C++11中,unordered_map作为一种关联容器,替代了hash_map,unordered_map的底层实现是\(hash\)表,所以被称为无序关联容器(源自baidu)
其实也很简单,也就是一种内部实现为\(hash\)表的\(STL\)容器,手写hash表呢就是先用前向星存起来,然后\(find\)函数一个for循环查找元素,也不会很难理解。
这道题是一道\(hash\)表模板题,比较基础。
code
解法①:\(hash表\)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
using namespace std;
inline int rd(){
int sign=1,res=0;char ch;
while((ch=getchar())<'0'||ch>'9')if(ch=='-')sign=-1;res=res*10+ch-48;
while((ch=getchar())>='0'&&ch<='9')res=res*10+ch-48;
return res*sign;
}
const int mod=200003;
ll cnt,n,m,t,h[200111],pre[200111];
ll nxt[200111],ans[200111];
void clear(){//清空数组
memset(pre,0,sizeof(pre));
memset(h,0,sizeof(h));
memset(nxt,0,sizeof(nxt));
}
void add(ll x){//前向星存图
int t=(x%mod+mod)%mod;//控制负数,这很重要
pre[++cnt]=x;
nxt[cnt]=h[t];
h[t]=cnt;
}
bool find(ll x){
ll t=(x%mod+mod)%mod;//同上
for(ll i=h[t];i;i=nxt[i])
if(pre[i]==x)//如果找到了这个数
return true;//就返回真
return false;//否则返回否
}
int tot;
bool flag;
int main(){
t=rd();
while(t--){
cnt=0,tot=0;
clear();
n=rd();
for(ll i=1,x;i<=n;i++){
x=rd();//读入进来
if(find(x))continue;//如果这个数出现过了,就进入下一次循坏
ans[++tot]=x,add(x);//否则就存起来,标记一下,ans,数组用来存答案控制格式
}
for(int i=1;i<=tot-1;i++)
printf("%d ",ans[i]);
printf("%d\n",ans[tot]);//完美输出
}
return 0;
}
解法②:STL(unordered_map)
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
inline int rd(){
int res=0,sign=1;char ch;
while((ch=getchar())<'0'||ch>'9')if(ch=='-')sign=-1;res=res*10+ch-48;
while((ch=getchar())>='0'&&ch<='9')res=res*10+ch-48;
return res*sign;
}
int t,n;
unordered_map<int,bool> a;
int main(){
t=rd();
while(t--){
a.clear();//清空
n=rd();
for(int i=1,x;i<=n;i++){
x=rd();
if(!a[x]){//如果没有标记过,即第一次出现时
printf("%d ",x);
a[x]=1;//就标记起来
}
}
cout<<endl;
}
return 0;
}
个人觉得STL还是好理解一点
完结撒花