数据结构整理

1. 树状数组

1.1. 作用

在O(logn)时间内:
1.求修改数组内单点数据
2.查询区间和

1.2. 板子

template <class T> struct fenwick_tree {
    using U = internal::to_unsigned_t<T>;

  public:
    fenwick_tree() : _n(0) {}
    explicit fenwick_tree(int n) : _n(n), data(n) {}

    void add(int p, T x) {
        assert(0 <= p && p < _n);
        p++;
        while (p <= _n) {
            data[p - 1] += U(x);
            p += p & -p;
        }
    }

    T sum(int l, int r) {
        assert(0 <= l && l <= r && r <= _n);
        return sum(r) - sum(l);
    }

  private:
    int _n;
    std::vector<U> data;

    U sum(int r) {
        U s = 0;
        while (r > 0) {
            s += data[r - 1];
            r -= r & -r;
        }
        return s;
    }
};

1.3. 使用实例

int main() {
    int n, q;
    scanf("%d %d", &n, &q);
    fenwick_tree<long long> fw(n);
    //相当于构造了个全是0的树状数组
    for (int i = 0; i < n; i++) {
        int a;
        scanf("%d", &a);
        fw.add(i, a); 
        //单点修改,将数组第i个值改为a
    }

    for (int i = 0; i < q; i++) {
        int t;
        scanf("%d", &t);
        if (t == 0) {
            int p, x;
            scanf("%d %d", &p, &x);
            fw.add(p, x);
            
        } else {
            int l, r;
            scanf("%d %d", &l, &r);
            printf("%lld\n", fw.sum(l, r));
            // 调用sum函数,求l到r的和
        }
    }
}

1.4. 树状数组(QQ版)板子


template <class T>
class Array_tree {
 public:
  Array_tree() {}
  Array_tree(int n) { this->n = n, tree = vector<T>(n + 1); }
  void add(int id, T key) {
    for (int i = id; i <= n; i += lowbit(i)) tree[i] += key;
  }

  T get_sum(int id) {
    T sum = 0;
    for (int i = id; i; i -= lowbit(i)) sum += tree[i];
    return sum;
  }

  T get_sum(int l, int r) { return get_sum(r) - get_sum(l - 1); }

 private:
  int n;
  vector<T> tree;
  int lowbit(int x) { return x & -x; }
};

1.5. 进阶用法

1.5.1. 权值树状数组

类似于权值线段树,但是常数比较小

1.5.2. 区间修改

1.5.2.1. 单点查询

使用树状数组来维护,单点增量,也就是维护一个差分数组,查询时只需要返回该位置原数加上增量即可

1.5.2.2. 区间查询

https://www.acwing.com/solution/content/44886/

2. 并查集

2.1. 作用

1.merge函数:合并两个并查集
2.same函数:判断两个点是否在同一个集合内
3.leader函数:返回这个这个点所在集合顶点函数
4.size函数:返回该点所在集合

2.2. 板子

struct dsu {
  public:
    dsu() : _n(0) {}
    explicit dsu(int n) : _n(n), parent_or_size(n, -1) {}

    int merge(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        int x = leader(a), y = leader(b);
        if (x == y) return x;
        if (-parent_or_size[x] < -parent_or_size[y]) std::swap(x, y);
        parent_or_size[x] += parent_or_size[y];
        parent_or_size[y] = x;
        return x;
    }

    bool same(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        return leader(a) == leader(b);
    }

    int leader(int a) {
        assert(0 <= a && a < _n);
        if (parent_or_size[a] < 0) return a;
        return parent_or_size[a] = leader(parent_or_size[a]);
    }

    int size(int a) {
        assert(0 <= a && a < _n);
        return -parent_or_size[leader(a)];
    }

    std::vector<std::vector<int>> groups() {
        std::vector<int> leader_buf(_n), group_size(_n);
        for (int i = 0; i < _n; i++) {
            leader_buf[i] = leader(i);
            group_size[leader_buf[i]]++;
        }
        std::vector<std::vector<int>> result(_n);
        for (int i = 0; i < _n; i++) {
            result[i].reserve(group_size[i]);
        }
        for (int i = 0; i < _n; i++) {
            result[leader_buf[i]].push_back(i);
        }
        result.erase(
            std::remove_if(result.begin(), result.end(),
                           [&](const std::vector<int>& v) { return v.empty(); }),
            result.end());
        return result;
    }

  private:
    int _n;
    // root node: -1 * component size
    // otherwise: parent
    std::vector<int> parent_or_size;
};

2.3. 使用实例

int solve() {
  int n, q;
  scanf("%d %d", &n, &q);
  dsu d(n);
  for (int i = 0; i < q; i++) {
    int u, v;
    cin >> u >> v;
    d.merge(u, v);//合并
  }
  //判断是否在同一个集合
  if (d.same(1, 2))
    cout << "YES\n";
  else
    cout << "NO\n";
  //返回父节点
  cout << d.leader(1) << endl;
  //返回集合大小
  cout << d.size(1) << endl;

  return 0;
}

输入

5 3
1 2
0 4
1 3

输出

NO
0
2 

2.4. 并查集(QQ版)板子

class Dsu {                            
  //下标从1开始
 public:
  vll fa, num;

  Dsu(int n) { fa = vll(n + 1), num = vll(n + 1, 1); }

  int find(int x) {
    if (!fa[x]) return x;
    return fa[x] = find(fa[x]);
  }

  // 合并
  bool Dunion(int p, int q) {
    int v = find(p), u = find(q);
    if (v == u) return 0;
    fa[u] = v;
    num[v] += num[u];
    num[u] = num[v];
    return 1;
  }

  ll num(int x) { return num[find(x)]; }
};

3. 线段树

3.1. 作用

对于一个满足下列性质的数组可生成线段树:(假设线段树需要维护的操作为 op(),比如维护区间和的op(a, b)为a + b)
1.满足结合率 即 op(op(a, b), c) = op(a, op(b, c))
2.需要一个幺元e 满足 op(e,a) = op(a, e),即e对任何数操作都为任何数, 比如0加任何数都为任何数

3.2. 板子(无lazy版)

namespace internal {
int ceil_pow2(int n) {
  int x = 0;
  while ((1U << x) < (unsigned int)(n)) x++;
  return x;
}
constexpr int bsf_constexpr(unsigned int n) {
  int x = 0;
  while (!(n & (1 << x))) x++;
  return x;
}
int bsf(unsigned int n) {
#ifdef _MSC_VER
  unsigned long index;
  _BitScanForward(&index, n);
  return index;
#else
  return __builtin_ctz(n);
#endif
}
}  // namespace internal
template <class S, S (*op)(S, S), S (*e)()>
struct segtree {
 public:
  segtree() : segtree(0) {}
  explicit segtree(int n) : segtree(std::vector<S>(n, e())) {}
  explicit segtree(const std::vector<S>& v) : _n(int(v.size())) {
    log = internal::ceil_pow2(_n);
    size = 1 << log;
    d = std::vector<S>(2 * size, e());
    for (int i = 0; i < _n; i++) d[size + i] = v[i];
    for (int i = size - 1; i >= 1; i--) {
      update(i);
    }
  }

  void set(int p, S x) {
    assert(0 <= p && p < _n);
    p += size;
    d[p] = x;
    for (int i = 1; i <= log; i++) update(p >> i);
  }

  S get(int p) const {
    assert(0 <= p && p < _n);
    return d[p + size];
  }

  S prod(int l, int r) const {
    assert(0 <= l && l <= r && r <= _n);
    S sml = e(), smr = e();
    l += size;
    r += size;

    while (l < r) {
      if (l & 1) sml = op(sml, d[l++]);
      if (r & 1) smr = op(d[--r], smr);
      l >>= 1;
      r >>= 1;
    }
    return op(sml, smr);
  }

  S all_prod() const { return d[1]; }

  template <bool (*f)(S)>
  int max_right(int l) const {
    return max_right(l, [](S x) { return f(x); });
  }
  template <class F>
  int max_right(int l, F f) const {
    assert(0 <= l && l <= _n);
    assert(f(e()));
    if (l == _n) return _n;
    l += size;
    S sm = e();
    do {
      while (l % 2 == 0) l >>= 1;
      if (!f(op(sm, d[l]))) {
        while (l < size) {
          l = (2 * l);
          if (f(op(sm, d[l]))) {
            sm = op(sm, d[l]);
            l++;
          }
        }
        return l - size;
      }
      sm = op(sm, d[l]);
      l++;
    } while ((l & -l) != l);
    return _n;
  }

  template <bool (*f)(S)>
  int min_left(int r) const {
    return min_left(r, [](S x) { return f(x); });
  }
  template <class F>
  int min_left(int r, F f) const {
    assert(0 <= r && r <= _n);
    assert(f(e()));
    if (r == 0) return 0;
    r += size;
    S sm = e();
    do {
      r--;
      while (r > 1 && (r % 2)) r >>= 1;
      if (!f(op(d[r], sm))) {
        while (r < size) {
          r = (2 * r + 1);
          if (f(op(d[r], sm))) {
            sm = op(d[r], sm);
            r--;
          }
        }
        return r + 1 - size;
      }
      sm = op(d[r], sm);
    } while ((r & -r) != r);
    return 0;
  }

 private:
  int _n, size, log;
  std::vector<S> d;

  void update(int k) { d[k] = op(d[2 * k], d[2 * k + 1]); }
};

3.3. 使用实例

ll op(ll a, ll b) { return a + b; };
ll e() { return 0; };

void solve() {
  cin >> n >> m;
  vll a(n + 1);
  for (int i = 1; i <= n; i++) cin >> a[i];
  segtree<ll, op, e> seg(a);
  while (m--) {
    ll t, x, y;
    cin >> t >> x >> y;
    if (t == 1)
      seg.set(x, seg.get(x) + y);
    else
      cout << seg.prod(x, y + 1) << endl;
  }
  return;
}
输入:
10 2
1 2 3 4 5 6 7 8 9 10
1 1 9
2 1 10
输出:
64

3.4. 线段树(QQ版)

该版方便魔改

class SGM_Tree {
 public:
  class point {
   public:
    ll sum, maxi, mini;
  };

  vll a, lazy;
  int n;
  ll sum, maxi, mini;
  vector<point> tree;

  SGM_Tree() {}
  SGM_Tree(int n, vi v) {
    // a下标默认从1开始,只需开n个点,不需要n + 1
    this->n = n;
    lazy = vll(n * 4 + 1);
    a.push_back(0);
    for (int i = 1; i <= n; i++) a.push_back(v[i]);
    tree = vector<point>(4 * n + 1), build(1, n, 1);
  }
  SGM_Tree(int n, vll v) {
    // a下标默认从1开始,只需开n个点,不需要n + 1
    this->n = n;
    lazy = vll(n * 4 + 1);
    a.push_back(0);
    for (int i = 1; i <= n; i++) a.push_back(v[i]);
    tree = vector<point>(4 * n + 1), build(1, n, 1);
  }
  SGM_Tree(int n, int* v) {
    // a下标默认从1开始,只需开n个点,不需要n + 1
    this->n = n;
    lazy = vll(n * 4 + 1);
    a.push_back(0);
    for (int i = 1; i <= n; i++) a.push_back(v[i]);
    tree = vector<point>(4 * n + 1), build(1, n, 1);
  }

  void push_up(int k) {
    int l = k * 2, r = k * 2 + 1;
    tree[k].sum = tree[l].sum + tree[r].sum;
    tree[k].maxi = max(tree[l].maxi, tree[r].maxi);
    tree[k].mini = min(tree[l].mini, tree[r].mini);
  }

  void push_down(int l, int r, int k) {
    if (lazy[k]) {
      int mid = l + r >> 1;
      lazy[k * 2] += lazy[k];
      lazy[k * 2 + 1] += lazy[k];
      tree[k * 2].sum += lazy[k] * (mid - l + 1);
      tree[k * 2 + 1].sum += lazy[k] * (r - mid);
      tree[k * 2].maxi += lazy[k];
      tree[k * 2 + 1].maxi += lazy[k];
      tree[k * 2].mini += lazy[k];
      tree[k * 2 + 1].mini += lazy[k];
      lazy[k] = 0;
    }
  }

  void get_updata(int l, int r, int k, ll value) {
    tree[k].sum += value * (r - l + 1);
    tree[k].maxi += value;
    tree[k].mini += value;
    lazy[k] += value;
  }

  void get(int k) {
    sum += tree[k].sum;
    maxi = max(maxi, tree[k].maxi);
    mini = min(mini, tree[k].mini);
  }

  void build(int l, int r, int k) {
    if (l == r) {
      tree[k].maxi = tree[k].mini = tree[k].sum = a[l];
      return;
    }
    int mid = l + r >> 1;
    build(l, mid, k * 2);
    build(mid + 1, r, k * 2 + 1);
    push_up(k);
  }

  void updata(int l, int r, int L, int R, int k, ll value) {
    if (L <= l && r <= R) {
      get_updata(l, r, k, value);
      return;
    }
    push_down(l, r, k);
    int mid = l + r >> 1;
    if (L <= mid) updata(l, mid, L, R, k * 2, value);
    if (R > mid) updata(mid + 1, r, L, R, k * 2 + 1, value);
    push_up(k);
  }

  void query(int l, int r, int L, int R, int k) {
    if (L <= l && r <= R) {
      get(k);
      return;
    }
    push_down(l, r, k);
    int mid = l + r >> 1;
    if (mid >= L) query(l, mid, L, R, 2 * k);
    if (mid < R) query(mid + 1, r, L, R, 2 * k + 1);
  }

  ll get_sum(int L, int R) {
    sum = 0;
    query(1, n, L, R, 1);
    return sum;
  }

  ll get_max(int L, int R) {
    maxi = -inf;
    query(1, n, L, R, 1);
    return maxi;
  }

  ll get_min(int L, int R) {
    mini = inf;
    query(1, n, L, R, 1);
    return mini;
  }
};

我的评价是,atc的真难用

3.5. 进阶用法

3.5.1. 权值线段树

https://blog.csdn.net/yanweiqi1754989931/article/details/117380913
简单来说就是维护一个数组,在一个值域内的数有多少个。

3.5.2. 区间维护gcd

3.5.3. 求逆序对

用权值线段树,首先建一个空树,然后每次加点,询问不符合该值大小的数量多少,总和便是逆序对数量。

4. st表

4.1. 作用

在O(logn)的时间复杂度将一个数组区间最值或gcd求出
每次查询O(1)

4.2. 板子

class st_map {
 public:
  st_map() {}
  st_map(vll v) {
    this->n = v.size(), this->a = v;
    this->st = vector<array<ll, 31>>(n + 1);
    st_init();
  }
  int query(int l, int r) {
    int len = r - l + 1;
    int k = log(len) / log(2);
    return max(st[l][k], st[r - (1 << k) + 1][k]);
  }

 private:
  int n;
  vll a;
  vector<array<ll, 31>> st;
  void st_init() {
    for (int j = 0; j <= 17; j++) {
      for (int i = 1; i + (1 << j) - 1 <= n; i++) {
        if (j == 0)
          st[i][j] = a[i];
        else
          st[i][j] = max(st[i][j - 1], st[i + (1 << j - 1)][j - 1]);
      }
    }
  }
};

5. 主席树(可持久线段树)

5.1. 作用

和线段树差不多,但是可以保存下所有的历史版本

5.2. 板子

template <class T>
class HJT_tree {
  //处理数据默认下标从1开始
 public:
  //构造函数
  HJT_tree() {}
  HJT_tree(vector<T> v) {
    base = v, this->n = base.size() - 1;
    tree = vector<node>(n * 32), root.push_back(build(1, n));
  }

  void updata(int v, int x, T value) {
    //插入函数(版本,修改位置,修改值)
    root.push_back(insert(root[v], 1, n, x, value));
  }

  T query(int v, int x) {
    //查询函数(版本,查询位置)
    return get_se(root[v], 1, n, x, x);
  }

  T query(int v, int l, int r) {
    //查询函数(版本,查询区间)
    return get_se(root[v], 1, n, l, r);
  }

 private:
  vi root;
  vector<T> base;
  int n, idx = 0;
  struct node {
    int l, r;
    T data;
  };
  vector<node> tree;
  void pushup(int q) { tree[q].data = op(tree[q].l, tree[q].r); }
  T op(int l, int r) { return max(tree[l].data, tree[r].data); }
  T e() { return -inf; }
  int build(int l, int r) {
    int now = ++idx, mid = l + r >> 1;
    if (l != r)
      tree[now].l = build(l, mid), tree[now].r = build(mid + 1, r), pushup(now);
    return now;
  }
  int insert(int old, int l, int r, int x, int value) {
    int now = ++idx, mid = l + r >> 1;
    tree[now] = tree[old];
    if (l == r)
      tree[now].data = value;
    else {
      if (x <= mid)
        tree[now].l = insert(tree[old].l, l, mid, x, value);
      else
        tree[now].r = insert(tree[old].r, mid + 1, r, x, value);
      pushup(now);
    }
    return now;
  }
  T get_se(int v, int l, int r, int L, int R) {
    if (L <= l && r <= R) return tree[v].data;
    ll mid = l + r >> 1;
    T res = e();
    if (L <= mid) res = max(res, get_se(tree[v].l, l, mid, L, R));
    if (R > mid) res = max(res, get_se(tree[v].r, mid + 1, r, L, R));
    return res;
  }
};

5.3. 使用实例

https://www.luogu.com.cn/record/84259274
此题卡输入,但是板子还是一样的

6. 平衡树

6.1. 作用

6.2. 板子

6.3. 使用实例

7. AC自动机和kmp

7.1. 作用

7.2. 板子

7.3. 使用实例

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值