DAY 3
DAY 3
数据结构
1.堆
Priority_queue 大根堆
Priority_queue<int , vector<int> , greater<int> > 小根堆
支持插入一个值,删除最大/最小值
它重载了运算符或函数类
堆排序 P1090 合并果子 哈夫曼树
3.ST 表
只查询区间最大最小值,不修改,静态的
定义mx[i][j] 是 i --> i + 2^j -1 的最大值
如果要求区间[L,R]的最大值
比如区间 [19 46]
先求区间长度 46-19+1=28
发现它可以被两个16覆盖
PS:求区间最大值重叠不影响
覆盖区间长P=Floor(log2(L))
把[L,R]拆成两个区间 [L,P]和[R-2^P,R],分成两半分别处理
比如递归求解max
st[i][j] = max(st[i][j-1], st[i + (1 << j-1)][j-1])
例题
1.P3865 【模板】ST表
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+10,logN=20; int n,m; int a[maxn],logg[maxn]={-1}; int f[maxn][logN+5]; inline int read() { int ans=0; char last=' ',ch=getchar(); while(ch<'0'||ch>'9') last=ch,ch=getchar(); while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar(); if(last=='-') ans=-ans; return ans; } int main() { n=read();m=read(); for(int i=1;i<=n;i++) a[i]=read(); for(int i=1;i<=n;i++) { f[i][0]=a[i]; logg[i]=logg[i>>1]+1; } for(int j=1;j<=logN;j++) for(int i=1;i+(1<<j)-1<=n;i++) f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]); for(int i=1;i<=m;i++) { int x=read(),y=read(); int k=logg[y-x+1]; printf("%d\n",max(f[x][k],f[y-(1<<k)+1][k])); } return 0; }
2.P2251 质量检测
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<cmath> #include<algorithm> #include<cstdlib> #include<queue> using namespace std; inline int read() { int ans=0; char last=' ',ch=getchar(); while(ch<'0'||ch>'9') last=ch,ch=getchar(); while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar(); if(last=='-') ans=-ans; return ans; } const int maxn=1e6+10,logN=20; int n,m; int a[maxn],q[maxn],logg[maxn]={-1}; int f[maxn][logN+5]; void ST() { for(int i=1;i<=n;i++) { f[i][0]=a[i]; logg[i]=logg[i>>1]+1; } for(int j=1;j<=logN;j++) for(int i=1;i+(1<<j)-1<=n;i++) f[i][j]=min(f[i][j-1],f[i+(1<<j-1)][j-1]); } int main() { n=read();m=read(); for(int i=1;i<=n;i++) a[i]=read(); ST(); int k=logg[m]; for(int i=1;i<=(n-m+1);i++) { int x=i,y=i+m-1; printf("%d\n",min(f[x][k],f[y-(1<<k)+1][k])); } return 0; }
4.HASH
是一种函数
平时说的HASH就是:设计一个函数F(字符串) à int ,就是把字符串变成数字,
map 基于比较函数的红黑树
如果你在map中放字符串,两个字符串比较的复杂度O(字符串长度)
所以要开发一种新的方法
假设给你一个字符串 ababb,1表示a,0表示b
10100
ababb
回忆初学二进制,用类似的方法计算出一个值
b*2^0+b*2^1+a*2^2+b*2^3+a*2^4
HASH允许冲突,我们要尽量避免冲突,而不是根治冲突
给你一个乱码 yy1926
我们现在构造HASH
先确定几进制 P ,一个大质数,比字符串集大
那么计算它的值就是 6*p^0+2*p^1+9*p^2+1*p^3+y*p^4+y*p^5
前面数字还好啊,直接int计算了,那么对于字符呢??
取其ASCII码,mod 998244353 (一个大质数)
不过为了方便,可以直接开unsigned ll
Unsigned ll范围 0 ~ 2^64 -1
你惊奇的发现2^64 -1是个质数
允许自然溢出,自然溢出相当于取模
用Unsigned ll存,用它计算,省去取模操作
为了避免冲突,你还可以取模两个数
HASH如何计算子串的hash?
希望设计一种算法,至少满足字符串拼接删除
比如现在有字符串 damengshen 和一个 p
d d
di d*p^1+i
din d*p^2+i*p^1+n
ding d*p^3+i*p^2+n*p^1+g
如果要求
ing的hash就是hash[ding]- hash[d]*p^3
ng的hash就是hash[ding]- hash[d]*p^2
我们维护了字符串每个前缀的hash值
发现p的指数难以确定,其实p的指数就是差的字符串的长度
令h[i]表示1~i的hash值
h[i]=h[i-1]*p+s[i]
hash[i][j]=h[j] - h[i-1]*p^(j-i+1)
5.并查集
没有必要保留树的结构,所以一个点的父亲可以直接指向它的代表源
路径压缩
- int father(int x){
- return fa[x]==x? x: fa[x]=father(fa[x]);
- }
Father[x]à代表源
并查集 P3367 【模板】并查集
6.树状数组
int lowbit(int x){ return x&(-x); } void modify(int x,int y){ // add y to a[x] for(int i=x;i<=n;i+=lowbit(i)) c[i]+=y; } int query(int x){ // sum of a[1]...a[x] int ret=0; for(int i=x;i;i-=lowbit(i)) ret+=c[i]; return ret; } int query(int l,int r){ return query(r)-query(l-1); }
7.线段树
例题:
- 求三个结点到一个结点距离之和最小的结点以及距离和
- 求出两两lca,其中有两个相同,答案则为另一个,画画图就可以理解
对于给出的一个数列
我们维护两个堆
大根堆堆顶维护中位数
考虑每次放两个数字进堆,比大根堆堆顶小的留在堆里,大的放到小根堆
一旦堆爆了,就把大根堆堆顶放到小根堆里啊
K叉哈夫曼树
用堆维护
你考虑把所有点取出来,与周围点连边,边权就是这两点海拔高度差,然后考虑排个序
单独去除边,不连边,然后一个一个往里边加入边,合并两个点为一个集合,当有一个集合的边数>=T,就确定了整个集合的等级
5.P5043 【模板】树同构([BJOI2015]树的同构)
对于每个点为根,求哈希
如果两个哈希集合相同,那么他们同构
- 树的HASH,如果两个哈希集合相同,那么他们同构
- 对于一棵无根树,它的重心个数不超过2。
- 枚举每个重心,以重心为根求出这棵有根树的最小表示,然后取字典序最大的即可。
- 对于有根树的最小表示,可以看成括号序列,每次把子树的括号序列按字典序排序后依次串连起来即可。
数据结构题目大赏 (一堆题目没做)