zoukankan      html  css  js  c++  java
  • 常见的数据结构梳理

    线段树

    复习了一会线段树, 觉得线段树最精妙的地方就在于 lazyTag 表达的含义

    lazyTag 打的标记是该节点的所有子节点, 不包括自己

    分析更新的正确性, 对于当前节点, 执行完 push_down 操作后, 可以保证其左右孩子上的 sum 值都是正确的

    所以最后一句 sum[o] = sum[lo] + sum[ro] 的正确性可以保障

    void updt(const int& L,const int& R,const int64_t& val, int o = 1,int l = 1,int r = n){
        if(L <= l and r <= R){
            sum[o] += (r - l + 1) * val;
            lz[o] += val;
            return;
        }
        push_down(o, l, r);
        if(L <= mid) updt(L, R, val, lo, l, mid);
        if(R > mid) updt(L, R, val, ro, mid + 1, r);
        sum[o] = sum[lo] + sum[ro];
    }
    

    考虑进行更新时经过的节点, 对于每一层, 最多只会经过两个节点, 因此复杂度是 (log n)

    树状数组

    树状数组的英文名是 Binary Indexed Trees

    image-20210914163339501

    对于 C[i] , 它维护的是一个以 i 结尾, 长度为 lowbit(i) 的区间和, 如上图所示

    查询 [1, x] 的区间和, 其实就是对 x 进行了一个从后往前的二进制拆分, 因为维护的是类似后缀的东西,

    显然 S[1,x] = C[x] + C[x - lowbit(x)] , 不停的跳即可

    对于一个单点的更新操作, 每次向上跳 lowbit , 可以保证是最小且有效的步数

    void add(int pos,int val){
        for(;pos <= n;pos += -pos & pos) C[pos] += val;
    }
    int query(int p){
        int ans = 0;
        for(;p;p -= -pos & pos) ans += C[p];
        return ans;
    }
    

    树状数组也可以用来区间加减,区间查询

    (A_i) 是原数组, (d_i = A_i - A_{i-1})(A) 的差分数组

    [S_n = sum_{i=1}^nA_i = d_1 + \d_1 + d_2 +\ d_1 + d_2 + d_3 + \ ... \d_1 + d_2 + d_3 + d_4 + ... + d_n \ = n (d_1 + d_2 + ... + d_n) - sum_{i=1}^nd_i(i-1) ]

    (B_i = d_i(i-1))

    [S_n = nsum d - sum B ]

    而当一段区间 ([L,R]) 一起加上 (x) 的时候,

    对于 (d) 来说,只有 (d_L)(d_{R+1}) 发生了变化

    对于 (B) 来说,也是同理,所以可以开两个树状数组来进行维护

    struct{
    	  ll C[N][2]; // 0 是差分d_i , 1 是 d_i * (i - 1)
    	  void add(int pos, ll val, int o) {
    	  	  for (; pos <= n; pos += (-pos) & pos) C[pos][o] += val;
    	  }
    	  ll ask(int pos, int o) {
    	  	  ll ans = 0;
    	  	  for (; pos; pos -= (-pos) & pos) ans += C[pos][o];
    	  	  return ans;
    	  }
    
    	  void updt(int l, int r, int x) {
    	  	  add(l, x, 0); add(r + 1, -x, 0);
    	  	  add(l, x * (l - 1), 1); add(r + 1, -x * (r), 1);
    	  }
    	  ll query(int l, int r) {
    	  	  ll R = r * ask(r, 0) - ask(r, 1);
    	  	  ll L = (l - 1) * ask(l - 1, 0) - ask(l - 1, 1);
    	  	  return R - L;
    	  }
    }BIT;
    
    

    主席树

    其实就是一个桶, 挂链的时候复制链的信息动态开点,查询区间 k 大的时候,对两棵树进行查询即可

    挂链可以挂成线性的可以挂成树形

    #include<bits/stdc++.h>
    #define mid (l+r>>1)
    using namespace std;
    
    const int maxn = 5e5 + 10;
    
    int sum[maxn << 5], L[maxn << 5], R[maxn << 5];
    int cnt;
    
    int a[maxn], id[maxn], root[maxn];
    
    int build(int l, int r) {
        int rt = ++cnt;
        sum[rt] = 0;
        if (l < r) {
            L[rt] = build(l, mid);
            R[rt] = build(mid + 1, r);
        }
        return rt;
    }
    
    int updt(int pre, int l, int r, int pos) {
        int rt = ++cnt;
        sum[rt] = sum[pre] + 1;
    
        R[rt] = R[pre];
        L[rt] = L[pre];
    
        if (l < r) {
            if (pos <= mid) {
                L[rt] = updt(L[pre], l, mid, pos);
            }
            else {
                R[rt] = updt(R[pre], mid + 1, r, pos);
            }
        }
        return rt;
    }
    
    int queryK(int x, int y, int l, int r, int k) {
        if (l == r) {
            return r;
        }
        int num = sum[L[y]] - sum[L[x]];
        if (num >= k) {
            return queryK(L[x], L[y], l, mid, k);
        }
        else {
            return queryK(R[x], R[y], mid + 1, r, k - num);
        }
    }
    
    
    
  • 相关阅读:
    判断是否在可视区域
    格式化数字10000,为10,000
    把数组(含有30项分成5组)
    排序
    enzyme 学习一
    golang使用sftp连接服务器远程上传、下载文件
    golang使用ssh远程连接服务器并执行命令
    一文弄懂vlan、三层交换机、网关、DNS、子网掩码、MAC地址的含义
    golang的序列化与反序列化的几种方式
    golang命令行参数解析
  • 原文地址:https://www.cnblogs.com/sduwh/p/15269469.html
Copyright © 2011-2022 走看看