DAY 3

DAY 3

数据结构

1.堆

Priority_queue   大根堆

Priority_queue<int , vector<int> , greater<int> >  小根堆

支持插入一个值,删除最大/最小值

它重载了运算符或函数类

堆排序 P1090 合并果子    哈夫曼树

 

 

2.LCA(最近公共祖先)

 

 

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;
}
Code

 

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;
}
Code

 

 

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);
}
欣赏一下dms的代码

 

 

 

7.线段树

 

 

 

 

 例题:

1.P4281 [AHOI2008]紧急集合 / 聚会

  • 求三个结点到一个结点距离之和最小的结点以及距离和
  • 求出两两lca,其中有两个相同,答案则为另一个,画画图就可以理解

 

2.P1168 中位数

  对于给出的一个数列

 我们维护两个堆

大根堆堆顶维护中位数

考虑每次放两个数字进堆,比大根堆堆顶小的留在堆里,大的放到小根堆

一旦堆爆了,就把大根堆堆顶放到小根堆里啊

 

 

 3.P2168 [NOI2015]荷马史诗

   K叉哈夫曼树

   用堆维护

 

 4.P3101 [USACO14JAN]滑雪等级

 你考虑把所有点取出来,与周围点连边,边权就是这两点海拔高度差,然后考虑排个序

单独去除边,不连边,然后一个一个往里边加入边,合并两个点为一个集合,当有一个集合的边数>=T,就确定了整个集合的等级

 

5.P5043 【模板】树同构([BJOI2015]树的同构)

    对于每个点为根,求哈希

    如果两个哈希集合相同,那么他们同构

  • 树的HASH,如果两个哈希集合相同,那么他们同构
  • 对于一棵无根树,它的重心个数不超过2。
  • 枚举每个重心,以重心为根求出这棵有根树的最小表示,然后取字典序最大的即可。
  • 对于有根树的最小表示,可以看成括号序列,每次把子树的括号序列按字典序排序后依次串连起来即可。

 

 

数据结构题目大赏 (一堆题目没做)

 
 

 

posted @ 2019-07-22 06:48  晔子  阅读(222)  评论(0编辑  收藏  举报