数据结构

stl库

st表

待总结

树状数组

一维

二维

待总结

线段树

普通线段树

lazy_tag

离散化

待总结

平衡树

AVL

待总结

红黑树

待总结

treap

旋转treap

1.插入x数

2.删除x数(若有多个相同的数,因只删除一个)

3.查询x数的排名(若有多个相同的数,因输出最小的排名)

4.查询排名为x的数

5.求x的前驱(前驱定义为小于x,且最大的数)

6.求x的后继(后继定义为大于x,且最小的数)


#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
#define pos(i,a,b) for(int i=(a);i<=(b);i++)
#define pos2(i,a,b) for(int i=(a);i>=(b);i--)
#define N 500010
struct Treap
{
       int l,r,w,v,size,rnd;
       //v为实际数值;rnd为优先级;size为以它为根的子树大小;w为自身节点存的数的个数(数据可以有多个重复的数)
}tree[N];
int n;
int root,size;
void update(int k)
{
     tree[k].size=tree[tree[k].l].size+tree[tree[k].r].size+tree[k].w;
     //更新子树大小
}
void rturn(int &k)
{
     int t=tree[k].l;
     tree[k].l=tree[t].r;
     tree[t].r=k;
     tree[t].size=tree[k].size;
     update(k);
     k=t;
}//右旋转
void lturn(int &k)
{
     int t=tree[k].r;
     tree[k].r=tree[t].l;
     tree[t].l=k;
     tree[t].size=tree[k].size;
     update(k);
     k=t;
}//左旋转
void insert(int &k,int x)
{
    if(k==0)
    {
       size++;
       k=size;
       tree[k].w=tree[k].size=1;
       tree[k].v=x;
       tree[k].rnd=rand();//随机数
       return;
    }
    tree[k].size++;
    if(tree[k].v==x)//如果有多个,w++
       tree[k].w++;
    else
    {
        if(tree[k].v<x)//满足二叉排序树性质
        {
           insert(tree[k].r,x);
           if(tree[tree[k].r].rnd<tree[k].rnd)
              lturn(k);//维护堆性质,左旋转
        }
        else
        {
            insert(tree[k].l,x);
            if(tree[tree[k].l].rnd<tree[k].rnd)
              rturn(k);
        }
    }
}
int tmp;
void query_pro(int k,int x)
{
    if(k==0)
      return;
    if(x>tree[k].v)
    {
       tmp=k;//不断更新过程。数值小于目标值,去右子树里找 ,找到第一个比它大的值。此时更新结果即为比它小的最大的值
       query_pro(tree[k].r,x);
    }
    else
      query_pro(tree[k].l,x);
}
void query_sub(int k,int x)
{
     if(k==0)
      return;
     if(x<tree[k].v)//与上面同理
     {
       tmp=k;
       query_sub(tree[k].l,x);
     }
     else
      query_sub(tree[k].r,x);
}
void del(int &k,int x)
{
     if(k==0)
       return;
     if(tree[k].v==x)
     {
        if(tree[k].w>1)//若不止相同值的个数有多个,删去一个
        {
          tree[k].w--;
          tree[k].size--;
          return;
        }
        if(tree[k].l*tree[k].r==0)//有一个儿子为空
          k=tree[k].l+tree[k].r;
        else
        {
            if(tree[tree[k].l].rnd<tree[k].rnd)
            {
               rturn(k);
               del(k,x);
            }
            else
            {
                lturn(k);
                del(k,x);
            }
        }
     }
     else
     {
         if(x>tree[k].v)
         {
            tree[k].size--;
            del(tree[k].r,x);
         }
         else
         {
             tree[k].size--;
             del(tree[k].l,x);
         }
     }
}
int query_rank(int k,int x)
{
    if(k==0)
      return 0;
    if(tree[k].v==x)
     return tree[tree[k].l].size+1;//找到目标值,左子树都比它小,左子树大小+1即为它的排名
    else
    {
        if(x>tree[k].v)
          return tree[tree[k].l].size+tree[k].w+query_rank(tree[k].r,x);
        else
          return query_rank(tree[k].l,x);
    }
}
int query_num(int k,int x)
{
    if(k==0)
      return 0;
    if(x<=tree[tree[k].l].size)
      return query_num(tree[k].l,x);
    else
      if(x>tree[tree[k].l].size+tree[k].w)
        return query_num(tree[k].r,x-tree[tree[k].l].size-tree[k].w);
      else
        return tree[k].v;
}
int main()
{
    //freopen("phs.in","r",stdin);
    //freopen("phs.out","w",stdout);
    scanf("%d",&n);
    int opt,x;
    pos(i,1,n)
    {
        scanf("%d%d",&opt,&x);
        switch(opt)
        {
        case 1:insert(root,x);break;
        case 2:del(root,x);break;
        case 3:printf("%d\n",query_rank(root,x));break;
        case 4:printf("%d\n",query_num(root,x));break;
        case 5:tmp=0;query_pro(root,x);printf("%d\n",tree[tmp].v);break;
        case 6:tmp=0;query_sub(root,x);printf("%d\n",tree[tmp].v);break;
        }
    }
    //while(1);
    return 0;
}

非旋转treap

参考范浩强谈数据结构
https://blog.csdn.net/u013482363/article/details/81484456

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define SF scanf
#define PF printf
#define MAXN 500010
using namespace std;
struct node{
    int ch[2];
    int key,sum,fix;
    node () {}
    node (int key1):key(key1),fix(rand()),sum(1) {}
}Tree[MAXN*30];
int ncnt,root[MAXN];
void update(int x){
    Tree[x].sum=Tree[Tree[x].ch[0]].sum+Tree[Tree[x].ch[1]].sum+1;
}
int Merge(int x,int y){
    if(x==0) return y;
    if(y==0) return x;
    if(Tree[x].fix<Tree[y].fix){
        Tree[x].ch[1]=Merge(Tree[x].ch[1],y);
        update(x);
        return x;
    }
    else{
        Tree[y].ch[0]=Merge(x,Tree[y].ch[0]);
        update(y);
        return y;
    }
}
pair<int,int> Split(int x,int k){
    if(x==0)
        return make_pair(0,0);
    int newone;
    pair<int,int> y;
    if(Tree[x].key<=k){
        newone=++ncnt;
        Tree[newone]=Tree[x];
        y=Split(Tree[newone].ch[1],k);
        Tree[newone].ch[1]=y.first;
        update(newone);
        y.first=newone;
    }
    else{
        newone=++ncnt;
        Tree[newone]=Tree[x];
        y=Split(Tree[newone].ch[0],k);
        Tree[newone].ch[0]=y.second;
        update(newone);
        y.second=newone;
    }
    return y;
}
int find_kth(int now,int val){
    pair<int,int> x=Split(root[now],val-1);
    int ans=Tree[x.first].sum+1;
    root[now]=Merge(x.first,x.second);
    return ans;
}
int get_kth(int x,int k){
    if(x==0)
        return 0;
    int ch0=Tree[x].ch[0];
    if(Tree[ch0].sum>=k)
        return get_kth(ch0,k);
    if(Tree[ch0].sum+1==k)
        return Tree[x].key;
    return get_kth(Tree[x].ch[1],k-Tree[ch0].sum-1);
}
int get_pre(int now,int val){
    int k=find_kth(now,val);
    return get_kth(root[now],k-1);
}
int get_bac(int now,int val){
    pair<int,int> x=Split(root[now],val);
    int ans=get_kth(x.second,1);
    root[now]=Merge(x.first,x.second);
    return ans;
}
void Insert(int val,int now){
    pair<int,int> x=Split(root[now],val);
    Tree[++ncnt]=node(val);
    Tree[ncnt].ch[0]=0;
    Tree[ncnt].ch[1]=0;
    root[now]=Merge(Merge(x.first,ncnt),x.second);
}
void Delete(int val,int now){
    pair<int,int> x=Split(root[now],val);
    pair<int,int> y=Split(x.first,val-1);
    if(Tree[y.second].key==val)
        y.second=Merge(Tree[y.second].ch[0],Tree[y.second].ch[1]);
    root[now]=Merge(Merge(y.first,y.second),x.second);
}
int n,las,x,tag;
int main(){
    SF("%d",&n);
    Insert(-2147483647,0);
    Insert(2147483647,0);
    for(int i=1;i<=n;i++){
        SF("%d%d%d",&las,&tag,&x);
        if(Tree[root[las]].sum!=0){
            root[i]=++ncnt;
            Tree[root[i]]=Tree[root[las]];
        }
        if(tag==1)
            Insert(x,i);
        if(tag==2)
            Delete(x,i);
        if(tag==3)
            PF("%d\n",find_kth(i,x)-1);
        if(tag==4)
            PF("%d\n",get_kth(root[i],x+1));
        if(tag==5)
            PF("%d\n",get_pre(i,x));
        if(tag==6)
            PF("%d\n",get_bac(i,x));
    }
}

线段树套平衡树

1.查询k在区间内的排名
2.查询区间内排名为k的值
3.修改某一位值上的数值
4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)
5.查询k在区间内的后继(后继定义为大于x,且最小的数)

#include<bits/stdc++.h>
#define ls(x) arr[x].child[0]
#define rs(x) arr[x].child[1]
using namespace std;
const int maxn = 5e4 + 5;
const int maxm = 25 * maxn;
const int INF = 2147483647;
typedef long long ll;
struct node {
    int val, sz, key, child[2];
} arr[maxm];
int tot;
void Push_Up(int idx) {
    arr[idx].sz = arr[ls(idx)].sz + arr[rs(idx)].sz + 1;
}
void Split(int root, int& x, int& y, int val) {
    if(!root) {
        x = y = 0;
        return;
    }
    if(arr[root].val <= val) x = root, Split(rs(root), rs(x), y, val);
    else y = root, Split(ls(root), x, ls(y), val);
    Push_Up(root);
}
void Merge(int& root, int x, int y) {
    if(!x || !y) {
        root = x + y;
        return;
    }
    if(arr[x].key < arr[y].key) root = x, Merge(rs(root), rs(x), y);
    else root = y, Merge(ls(root), x, ls(y));
    Push_Up(root);
}
void Insert(int& root, int val) {
    int x = 0, y = 0, z = ++tot;
    arr[z].val = val, arr[z].sz = 1, arr[z].key = rand();
    Split(root, x, y, val);
    Merge(x, x, z);
    Merge(root, x, y);
}
void Erase(int& root, int val) {
    int x = 0, y = 0, z = 0;
    Split(root, x, y, val);
    Split(x, x, z, val - 1);
    Merge(z, ls(z), rs(z));
    Merge(x, x, z);
    Merge(root, x, y);
}
int Kth_number(int root, int k) {
    while(arr[ls(root)].sz + 1 != k) {
        if(arr[ls(root)].sz >= k) root = ls(root);
        else k -= arr[ls(root)].sz + 1, root = rs(root);
    }
    return arr[root].val;
}
int Get_rank(int& root, int val) {
    int x = 0, y = 0;
    Split(root, x, y, val - 1);
    int res = arr[x].sz;
    Merge(root, x, y);
    return res;
}
int Pre(int& root, int val) {
    int x = 0, y = 0;
    Split(root, x, y, val - 1);
    int res = -INF;
    if(arr[x].sz) res = Kth_number(x, arr[x].sz);
    Merge(root, x, y);
    return res;
}
int Suf(int& root, int val) {
    int x = 0, y = 0;
    Split(root, x, y, val);
    int res = INF;
    if(arr[y].sz) res = Kth_number(y, 1);
    Merge(root, x, y);
    return res;
}
int T[4 * maxn], a[maxn];
int n, m, op, l, r, x, pos;
void Add(int idx, int L, int R, int pos, int val) {
    Insert(T[idx], val);
    if(L == R) return;
    int mid = L + R >> 1;
    if(mid >= pos) Add(idx << 1, L, mid, pos, val);
    else Add(idx << 1 | 1, mid + 1, R, pos, val);
}
void Delete(int idx, int L, int R, int pos, int val) {
    Erase(T[idx], val);
    if(L == R) return;
    int mid = L + R >> 1;
    if(mid >= pos) Delete(idx << 1, L, mid, pos, val);
    else Delete(idx << 1 | 1, mid + 1, R, pos, val);
}
int Query_Rank(int idx, int L, int R, int QL, int QR, int val) {
    if(L >= QL && R <= QR) return Get_rank(T[idx], val);
    int mid = L + R >> 1, res = 0;
    if(mid >= QL) res += Query_Rank(idx << 1, L, mid, QL, QR, val);
    if(QR > mid) res += Query_Rank(idx << 1 | 1, mid + 1, R, QL, QR, val);
    return res;
}
int Query_Pre(int idx, int L, int R, int QL, int QR, int val) {
    if(L >= QL && R <= QR) return Pre(T[idx], val);
    int mid = L + R >> 1, res = -INF;
    if(mid >= QL) res = max(res, Query_Pre(idx << 1, L, mid, QL, QR, val));
    if(QR > mid) res = max(res, Query_Pre(idx << 1 | 1, mid + 1, R, QL, QR, val));
    return res;
}
int Query_Suf(int idx, int L, int R, int QL, int QR, int val) {
    if(L >= QL && R <= QR) return Suf(T[idx], val);
    int mid = L + R >> 1, res = INF;
    if(mid >= QL) res = min(res, Query_Suf(idx << 1, L, mid, QL, QR, val));
    if(QR > mid) res = min(res, Query_Suf(idx << 1 | 1, mid + 1, R, QL, QR, val));
    return res;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    srand(19260817);
    cin >> n >> m;
    for(int i = 1; i <= n; ++i) {
        cin >> a[i];
        Add(1, 1, n, i, a[i]);
    }
    while(m--) {
        cin >> op;
        if(op == 1) {
            cin >> l >> r >> x;
            cout << Query_Rank(1, 1, n, l, r, x) + 1 << endl;
        }
        else if(op == 2) {
            cin >> l >> r >> x;
            int L = 0, R = 1e8;
            while(L < R) {
                int mid = (L + R + 1) >> 1;
                if(Query_Rank(1, 1, n, l, r, mid) < x) L = mid;
                else R = mid - 1;
            }
            cout << R << endl;
        }
        else if(op == 3) {
            cin >> pos >> x;
            Delete(1, 1, n, pos, a[pos]);
            a[pos] = x;
            Add(1, 1, n, pos, a[pos]);
        }
        else if(op == 4) {
            cin >> l >> r >> x;
            cout << Query_Pre(1, 1, n, l, r, x) << endl;
        }
        else if(op == 5) {
            cin >> l >> r >> x;
            cout << Query_Suf(1, 1, n, l, r, x) << endl;
        }
    }
}

splay伸展树

https://blog.csdn.net/VictoryCzt/article/details/83787494

替罪羊树

待学

左偏树

可并堆
05年黄源河论文
待总结

主席树

权值线段树(维护一段区间的数的出现次数,可用于查寻区间所有属的第k大)
主席树相当于n棵权值线段树,但实际上是在一颗权值线段树上加边,可维护静态区间第k大
主席树套树状数组可维护动态区间第k大
待总结

划分树

待总结

kd树

待学

珂朵莉数

待学

动态树

待学

园方树

待学

仙人掌树

待学

树分治

参考09年漆子超的论文

点分治

题意:一棵有n个节点的树,每条边有个权值代表相邻2个点的距离,要求求出所有距离不超过k的点对(u,v)

题解:树分治
假设树以root为根节点,那么满足要求的点对有2种情况:
①路径经过root且dis(u,v)<=k
②路径不经过root,即其路径的最高点为子树上某一节点

对于第②种情况可以通过递归求解,这里只讨论第一种情况
该如何求解路径经过root且dis(u,v)<=k的合法点对数呢?

设dir[u]为u到根节点root的距离,那么只有满足dir[u]+dir[v]<=k且LCA(u,v)==root的点对才是合法的,
设cnt1=树中所有dis(u,v)<=k的点对数,cnt2=LCA(u,v)==root的子节点的合法点对数
那么以root为根的树种合法点对数为:ans=cnt1-cnt2
找出有多少个dir[u]+dir[v]的方法很简单:只需要排序后扫一遍即可。

总结一下算法的过程:
①计算以u为根的树种每棵子树的大小
②根据子树大小找出树的重心root(以树的重心为根的树,可以使其根的子树中节点最多的子树的节点最少)
③以root为根,计算树中每个点到root的距离dir
④计算树中所有满足dir[u]+dir[v]<=k的点对数cnt1
⑤计算以root的子节点为根的子树中,满足dir[u]+dir[v]<=k的点对数cnt2
⑥ans+=cnt1-cnt2
注意:每次计算完cnt1后,要将vis[root]=1,这样就可以将一棵树分解成若干棵子树

#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int MX = 1e4 + 5;
 
struct Edge {
    int v, w, nxt;
} E[MX * 2];
 
int n, k, root, Max, ans;
vector <int> dis;
int sz[MX], maxv[MX], head[MX], tot;
bool vis[MX];
 
void init() {
    memset(vis, false, sizeof(vis));
    memset(head, -1, sizeof(head));
    tot = 0;
}
void add(int u, int v, int w) {
    E[tot].v = v;
    E[tot].w = w;
    E[tot].nxt = head[u];
    head[u] = tot++;
}
 
 
void dfs_size(int u, int fa) {
    sz[u] = 1; maxv[u] = 0;
    for (int i = head[u]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        if (vis[v] || v == fa) continue;
        dfs_size(v, u);
        sz[u] += sz[v];
        maxv[u] = max(maxv[u], sz[v]);
    }
}
 
void dfs_root(int r, int u, int pre) {  // 找出以u为根的子树的重心
    maxv[u] = max(maxv[u], sz[r] - sz[u]);
    if (Max > maxv[u]) {
        Max = maxv[u];
        root = u;
    }
    for (int i = head[u]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        if (v == pre || vis[v]) continue;
        dfs_root(r, v, u);
    }
}
 
void dfs_dis(int u, int fa, int dir) {
    dis.push_back(dir);
    for (int i = head[u]; ~i; i = E[i].nxt) {
        int v = E[i].v, w = E[i].w;
        if (vis[v] || v == fa) continue;
        dfs_dis(v, u, dir + w);
    }
}
 
int cal(int rt, int d) {
    dis.clear();
    dfs_dis(rt, -1, d);
    sort(dis.begin(), dis.end());
    int i = 0, j = dis.size() - 1, ret = 0;
    while (i < j) {
        while (dis[i] + dis[j] > k && i < j) j--;
        ret += j - i;
        i++;
    }
    return ret;
}
 
void DFS(int u) {
    Max = n;
    dfs_size(u, -1);
    dfs_root(u, u, -1);
    int rt = root;
    ans += cal(rt, 0);
    vis[rt] = 1;
    for (int i = head[rt]; ~i; i = E[i].nxt) {
        int v = E[i].v, w = E[i].w;
        if (vis[v]) continue;
        ans -= cal(v, w);
        DFS(v);
    }
}
 
int main() {
    //freopen("in.txt","r",stdin);
    while (scanf("%d%d", &n, &k), n || k) {
        init();
        for (int i = 1; i < n; i++) {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            add(u, v, w); add(v, u, w);
        }
        ans = 0;
        DFS(1);
        printf("%d\n", ans);
    }
    return 0;
}

边分治

待总结
https://blog.csdn.net/qq_31759205/article/details/75579558

树链剖分

dfs序

随后写

树链剖分

https://blog.csdn.net/liyizhixl/article/details/69668791
b站电子科大算法讲堂
待总结

树套树

待学

pb_ds库

于纪平ppt
https://blog.csdn.net/riba2534/article/details/80454602

先挖坑,待更。。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值