文章目录
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
先挖坑,待更。。。