基础数据结构 学习笔记

1. 栈

1.1 栈的性质

后进先出。

1.2 栈处理括号序列

一个栈,遇到左括号压入栈,遇到右括号判断栈顶是否为左括号,如果是则弹出栈顶,否则不合法。

bool flag=1;
    for(int i=0; i<s.size(); i++) {
        if(s[i]=='(') st.push(s[i]);
        else if(st.top()=='(') st.pop();
        else flag=0;
    }
    if(st.size()) flag=0;
    puts(flag?"YES":"NO");
    return 0;

1.3 单调栈

单调队列平替。例如维护一个顶小的单调栈,插入 \(x\) 时先将 \(<x\) 的弹出,然后插入。常用于优化 dp。

2. 队列

2.1 队列的性质

先进先出。

2.2 单调队列

维护一个头小尾大的单调队列,插入 \(x\) 时从队末将 \(>x\) 的弹出,然后插入。常用于优化 dp。

	int head=1,tail=0;
    //in a[x]
	while(head<=tail && a[q[tail]]>a[x]) tail--;
   q[++tail]=x;
   while(head<=tail && x-q[head]>=t/*不满足条件*/) head++;

3. 并查集

值得注意的是路径压缩会破坏树的形态。

struct easy_dsu {
    int fa[maxn];
    int find(int x) {
        while(fa[x]^x) x=fa[x]=fa[fa[x]];
        return x;
    }
    void init(int n) {
        for(int i=1; i<=n; i++) fa[i]=i;
    }
    void merge(int x,int y) {
        int rx=find(x),ry=find(y);
        fa[rx]=ry;
    }
    bool query(int x,int y) {
        return find(x)==find(y);
    }
};

struct size_dsu {
    int fa[maxn],sz[maxn];
    int find(int x) {
        while(fa[x]^x) x=fa[x];
        return x;
    }
    void init(int n) {
        for(int i=1; i<=n; i++) fa[i]=i,sz[i]=1;
    }
    void merge(int x,int y) {
        int rx=find(x),ry=find(y);
        sz[ry]+=rx;
        fa[rx]=ry;
    }
    bool query(int x,int y) {
        return find(x)==find(y);
    }
};

struct dis_dsu {
    int fa[maxn],dis[maxn];
    int find(int x) {
        if(fa[x]!=x) {
            int y=find(fa[x]);
            dis[x]+=dis[fa[x]];
            fa[x]=y;
        }
        return fa[x];
    }
    void init(int n) {
        for(int i=1; i<=n; i++) fa[i]=i,dis[i]=0;
    }
    void merge(int x,int y) {
        int rx=find(x),ry=find(y);
        fa[rx]=ry;
    }
    bool query(int x,int y) {
        return find(x)==find(y);
    }
};

struct merge_by_rank {
    //不会破坏树的形态
    int fa[maxn],rk[maxn];
    int find(int x) {
        while(fa[x]^x) x=fa[x];
        return x;
    }
    void init(int n) {
        for(int i=1; i<=n; i++) fa[i]=i,rk[i]=0;
    }
    void merge(int x,int y) {
        int rx=find(x),ry=find(y);
        if(rx^ry) {
            if(rk[rx]<rk[ry]) fa[rx]=ry;
            else if(rk[ry]<rk[rx]) fa[ry]=rx;
            else fa[rx]=ry,rk[ry]++;
        }
    }
    bool query(int x,int y) {
        return find(x)==find(y);
    }
};

4. hash 表

给一个数处理一个 hash 值,有奇妙的用处。

CF1746F

考虑如果一段区间合法,那么你对每个数值进行哈希,这段区间哈希值的和必定是 \(k\) 的倍数。注意到这只是充分条件,不能反推,所以多哈希几次就行。

ABC339F

对每个数hash一下,暴力匹配。

5. 链表

实现相邻访问 \(O(1)\),快速从头尾删除、插入,快速随机访问。

6. ST表

一个很像 dp 的数据结构,可以做到 \(O(n\log n)-O(1)\) 维护区间最大值、最小值、gcd等可重复性的信息。

P3865 为例。

\(f_{i,j}\) 表示从 \(i\) 开始 \(2^j\) 个位置的最大值。

那么可以得出状态转移方程 \(f_{i,j}=\max(f_{i,j-1},f_{i+2^j-1,j-1})\)

那么可以 \(O(n\log n)\) 转移。

考虑查询:可以直接查询 \([x,x+2^k]\)\([y-2^k+1,y]\),其中 \(k=\log_2(y-x+1)\)

最小值同理。

同样,我们也可以维护区间 gcd,因为 gcd 重复算肯定是可以的。P1890

posted @ 2024-02-29 10:00  lgh_2009  阅读(6)  评论(0编辑  收藏  举报