关于数据结构一些自己的理解与总结与题目

1.并查集
2.线段树 扫描线 主席树等
3.trie树 可持久化trie树
4.散列表
5.字符串哈希
6.模拟堆
7.莫队
8.双向链表
9.树链剖分
10.左偏树(可合并堆)
11.克鲁斯卡尔重构树

关于并查集:
一定是他的集合与集合里面的最顶层的父亲的关系
若是权值并查集 一定是父亲的值相加减 因为成员的值不一定准确

acwing 239
题目大意 给你
一个n 假设有一个01序列
给你一个m 给你m个条件 问你第几次条件的时候是矛盾的
string a
m的 格式是 给你一个 l r 和 a
a为even 表示 l~r 里面有偶数个1

思路有两种 1.用前缀和的想法 若 l ~ r为偶数 那么前缀 l - 1 与 前缀 r 的 1的个数相同 若l ~ r为奇数 则不同

2.存一个集合 fa[i] 表示 i为偶数 fa[i + n] 表示 为奇数
那么若 l ~ r为偶数 那么 fa[i] = fa[j] fa[i + n] = fa[j + n]
若为奇数 fa[i] = fa[j + n] fa[i + n] = fa[j + n]


关于线段树
线段树则需看清 条件要求的 是否可以由左儿子 右儿子 来递归完成

例如线段树维护区间最大公约数 由于公约数就是gcd(a1,a2,a3,a4…an)所以可以维护
若加上区间修改的话 那就需要用到性质 gcd(a1,a2…an) = gcd(a1,a2 - a1,a3 - a2…an - an - 1);
那么那么利用差分的性质 我们每次都只需要单点修改 就行了

线段树扫描线
主要求得问题是矩阵覆盖面积或者周长问题

步骤是
1.将所有的竖着得线段储存下来 并且按照从大到小排序 若数据有小数或者大数 则离散化
2.每一个矩阵得左边得边赋值为 1 右边的边赋值为2
3.从做往右遍历边 进行区间修改
4.注意!!! 由于扫描线是根据区间进行操作 列如 1 - 5 这个区间 + 1 的话 只需要将 1 - 4 + 1就行 因为 点1表示的其实是 1 ~ 2 表示的是1 ~ 2这段区间
周长的模板

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N = 2e4 + 100;

struct node{
    
    
    int x,cnt;
}tree[N * 4];

struct edg{
    
    
    int x,y1,y2,type;
    bool operator < (const edg &p)const{
    
    
        if(x == p.x) return type > p.type;
        return x < p.x;
    }
}edge[N];

struct ed{
    
    
    int x1,x2,y,type;
    bool operator < (const ed &p)const{
    
    
        if(y == p.y) return type > p.type;
        return y < p.y;
    }
}edge2[N];

void build(int s,int t,int p){
    
    
    tree[p].cnt = 0,tree[p].x = 0;
    if(s == t) return;
    int mid = s + t >> 1;
    build(s,mid,p * 2);
    build(mid + 1,t,p * 2 + 1);
}

void push_up(int s,int t,int p){
    
    
    if(tree[p].cnt){
    
    
        tree[p].x = (t - s + 1);
    }else if(s != t){
    
    
        tree[p].x = tree[p * 2].x + tree[p * 2 + 1].x;
    }
    else tree[p].x = 0;
}

void update(int s,int t,int l,int r,int p,int x){
    
    
    if(s >= l && t <= r){
    
    
        tree[p].cnt += x;
        push_up(s,t,p);
        return;
    }
    int mid = s + t>> 1;
    if(l <= mid) update(s,mid,l,r,p * 2,x);
    if(mid < r) update(mid + 1,t,l,r,p * 2 + 1,x);
    push_up(s,t,p);
    return;
}

int main(){
    
    
    int n;
    cin >> n;
    int cnt = 0;
    for(int i = 1; i <= n; i++){
    
    
        int zx,zy,yx,yy;
        cin >> zx >> zy >> yx >> yy;
        zx += 10001,zy += 10001,yx += 10001,yy += 10001;
        edge[++cnt] = {
    
    zx,zy,yy,1};
        edge2[cnt] = {
    
    zx,yx,zy,1};
        edge[++cnt] = {
    
    yx,zy,yy,-1};
        edge2[cnt] = {
    
    zx,yx,yy,-1};
    }

    sort(edge + 1,edge + cnt + 1);
    sort(edge2 + 1,edge2 + cnt + 1);
    build(1,20001,1);
    int sum = 0;
    int now = 0,pre = 0;
    for(int i = 1; i <= cnt; i++){
    
    
        pre = tree[1].x;
        update(1,20001,edge[i].y1,edge[i].y2 - 1,1,edge[i].type);
        now = tree[1].x;
        sum += abs(now - pre);
    }
    build(1,20001,1);

    for(int i = 1; i <= cnt; i++){
    
    
        pre = tree[1].x;
        update(1,20001,edge2[i].x1,edge2[i].x2 - 1,1,edge2[i].type);
        now = tree[1].x;
        sum += abs(now - pre);
    }

    cout << sum << endl;



    return 0;
}

面积并模板!

#include<iostream>
#include<algorithm> 
#include<map> 

using namespace std;

const int N = (1e5 + 10) * 4;

typedef long long ll;

struct edge{
    
    
    double x,y1,y2;int tape;
    bool operator < (const edge &p)const{
    
    
        return x < p.x;
    }
}a[N];

double b[N];int cnt;

struct node{
    
    
    double x;int cnt; 
}tree[N];

void pushup(int s,int t,int p){
    
    
    if(tree[p].cnt){
    
    
        tree[p].x = b[t + 1] - b[s];
    }else if(s != t){
    
    
        tree[p].x = tree[p * 2].x + tree[p * 2 + 1].x;
    }else tree[p].x = 0;
}

void update(int s,int t,int l,int r,int p,int x){
    
    
    if(s >= l && t <= r){
    
    
        tree[p].cnt += x;
        pushup(s,t,p);
        return;
    }
    int mid = s + t >> 1;
    if(l <= mid) update(s,mid,l,r,p * 2,x);
    if(mid < r) update(mid + 1,t,l,r,p * 2 + 1,x);
    pushup(s,t,p);
    return;
}

int main(){
    
    
    int t,T = 0;
    while(cin >> t && t){
    
    
        cnt = 0;
        for(int i = 1; i <= t; i++){
    
    
            double x1,y1,x2,y2;
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            b[++cnt] = y1,a[cnt] = {
    
    x1,y1,y2,1};
            b[++cnt] = y2,a[cnt] = {
    
    x2,y1,y2,-1};
        }

        sort(b + 1,b + cnt + 1);
        sort(a + 1,a + cnt + 1);

        int tot = unique(b + 1,b + cnt + 1) - b - 1;

        double res = 0;
        for(int i = 1; i <= t * 2; i++){
    
    
            int k1 = lower_bound(b + 1,b + tot + 1,a[i].y1) - b;
            int k2 = lower_bound(b + 1,b + tot + 1,a[i].y2) - b;
            if(i > 1){
    
    
                res += tree[1].x * (a[i].x - a[i - 1].x);
            }
            update(1,tot,k1,k2 - 1,1,a[i].tape);
        }

        printf("Test case #%d\n", ++T);
        printf("Total explored area: %.2lf\n\n", res);

    }

    return 0;
}

线段树的加减乘

#include<iostream>

using namespace std;

const int N = (1e5 + 10) * 4;

typedef long long ll;

int n,m;ll a[N];

ll tree[N],lazyc[N],lazyj[N];

void build(int s,int t,int p){
    
    
	lazyc[p] = 1;
	lazyj[p] = 0;
	if(s == t){
    
    
		tree[p] = a[s] % m;
		return;
	}
	int mid = s + t >> 1;
	build(s,mid,p * 2);
	build(mid + 1,t,p * 2 + 1);
	tree[p] = (tree[p * 2] + tree[p * 2 + 1]) % m; 
	return;	
}

void pushdown(int s,int t,int p){
    
    
	int mid = s + t >> 1;
	tree[p * 2] = tree[p * 2] * lazyc[p] % m;
	lazyj[p * 2] = lazyj[p * 2] * lazyc[p] % m;
	lazyj[p * 2 + 1] = lazyj[p * 2 + 1] * lazyc[p] % m;
	tree[p * 2 + 1] = tree[p * 2 + 1] * lazyc[p] % m;
	lazyc[p * 2] = lazyc[p * 2] * lazyc[p] % m;
	lazyc[p * 2 + 1] = lazyc[p * 2 + 1] * lazyc[p] % m;
	lazyc[p] = 1;
	tree[p * 2] = (tree[p * 2] + lazyj[p] * (mid - s + 1) % m) % m;
	tree[p * 2 + 1] = (tree[p * 2 + 1] + lazyj[p] * (t - mid) % m) % m;
	lazyj[p * 2] = (lazyj[p * 2] + lazyj[p]) % m;
	lazyj[p * 2 + 1] = (lazyj[p * 2 + 1] + lazyj[p]) % m;
	lazyj[p] = 0;
}

void pushup(int p){
    
    
	tree[p] = (tree[p * 2] + tree[p * 2 + 1]) % m; 
	return;
}

void updatec(int s,int t,int l,int r,int p,ll x){
    
    
	if(s >= l && t <= r){
    
    
		tree[p] = tree[p] * x % m;
		lazyc[p] = lazyc[p] * x % m;
		lazyj[p] = lazyj[p] * x % m;
		return;
	}
	pushdown(s,t,p);
	int mid = s + t >> 1;
	if(l <= mid) updatec(s,mid,l,r,p * 2,x);
	if(mid < r) updatec(mid + 1,t,l,r,p * 2 + 1,x);
	pushup(p);
	return;
}

void updatej(int s,int t,int l,int r,int p,ll x){
    
    
	if(s >= l && t <= r){
    
    
		tree[p] = (tree[p] + (t - s + 1) * x % m) % m;
		lazyj[p] = (lazyj[p] + x) % m;
		return;
	}
	pushdown(s,t,p);
	int mid = s + t >> 1;
	if(l <= mid) updatej(s,mid,l,r,p * 2,x);
	if(mid < r) updatej(mid + 1,t,l,r,p * 2 + 1,x);
	pushup(p);
	return;
}

ll getsum(int s,int t,int l,int r,int p){
    
    
	ll sum = 0;
	if(s >= l && t <= r){
    
    
		return tree[p];
	}
	pushdown(s,t,p);
	int mid = s + t >> 1;
	if(l <= mid) sum = getsum(s,mid,l,r,p * 2) % m;
	if(mid < r) sum = (sum + getsum(mid + 1,t,l,r,p * 2 + 1)) % m;
	return sum;
}

int main(){
    
    
	scanf("%d%d",&n,&m);
	
	for(int i = 1; i <= n; i++){
    
    
		scanf("%lld",&a[i]); 
	}
	
	build(1,n,1);
	
	int t ;
	cin >> t;
	while(t--){
    
    
		int op,x,y;ll z;
		scanf("%d%d%d",&op,&x,&y);
		if(op == 1){
    
    
			scanf("%lld",&z);
			updatec(1,n,x,y,1,z);
		}else if(op == 2){
    
    
			scanf("%lld",&z);
			updatej(1,n,x,y,1,z);
		}else{
    
    
			printf("%lld\n",getsum(1,n,x,y,1));
		}

	}
	
	return 0;
}

主席树 用来查询静态区间第k值
利用前缀和思想 分别存入 包括前 i 个树的 权值线段树
每次只需要修改 第i棵树 对于 第 i - 1个树的 不同的地方

#include<iostream>
#include<algorithm>

using namespace std;

const int N = 1e5 + 10;

struct node{
    
    
	int l,r,v;
}tree[N * 25];

int a[N],b[N];

int tot,T[N];//T表示第i课树的 首结点
int build(int l,int r){
    
    
	int p = ++tot;
	if(l < r){
    
    
		int mid = l + r >> 1;
		tree[p].l = build(l,mid);
		tree[p].r = build(mid + 1,r);
	}
	tree[p].v = 0;
	return p;
}

int update(int pre,int l,int r,int x){
    
    
	int p = ++tot;
	tree[p].l = tree[pre].l,tree[p].r = tree[pre].r,tree[p].v = tree[pre].v + 1;
	if(l < r){
    
    
		int mid = l + r >> 1;
		if(x <= mid) tree[p].l = update(tree[pre].l,l,mid,x);
		else tree[p].r = update(tree[pre].r,mid + 1,r,x);
	}
	return p;
}

int query(int x,int y,int l,int r,int k){
    
    
	if(l == r) return l;
	int sum = tree[tree[y].l].v - tree[tree[x].l].v;
	int mid = l + r >> 1;
	if(k <= sum) return query(tree[x].l,tree[y].l,l,mid,k);
	else return query(tree[x].r,tree[y].r,mid + 1,r,k - sum);
}

int main(){
    
    
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= n; i++){
    
    
		scanf("%d",&a[i]);
		b[i] = a[i];
	}
	
	sort(b + 1,b + n + 1);
	int cnt = unique(b + 1,b + n + 1) - b - 1;
	
	T[0] = build(1,cnt);
	for(int i = 1; i <= n; i++){
    
    
		int s = lower_bound(b + 1,b + cnt + 1,a[i]) - b;
		T[i] = update(T[i - 1],1,cnt,s);
	}
	
	for(int i = 1; i <= m; i++){
    
    
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		printf("%d\n",b[query(T[x - 1],T[y],1,cnt,z)]); //求得是 x,y区间的第Z大值
	}
	
	
	
	
	return 0;
} 

主席数求区间前k大值得前缀和

int build(int l,int r){
    
    
	int p = ++tot;
	tree[p].s = tree[p].val = tree[p].v = 0;
	if(l < r){
    
    
		int mid = l + r >> 1;
		tree[p].l = build(l,mid);
		tree[p].r = build(mid + 1,r);
	}
	return p;
}

int update(int pre,int l,int r,int x){
    
    
	int p = ++tot;
	tree[p].l = tree[pre].l,tree[p].r = tree[pre].r,tree[p].v = tree[pre].v + 1;
	tree[p].s = tree[pre].s + m[x];
	
	if(l == r){
    
    
		tree[p].val = m[x];
		return p;
	}
	if(l < r){
    
    
		int mid = l + r >> 1;
		if(x <= mid) tree[p].l = update(tree[pre].l,l,mid,x);
		else tree[p].r = update(tree[pre].r,mid + 1,r,x);
	}
	
	return p;
}

ll query(int x,int y,int l,int r,int k){
    
    
	if(l == r){
    
    
		return tree[y].val * k; //防止k有很多个
	}
	int sum = tree[tree[y].r].v - tree[tree[x].r].v;
	int mid = l + r >> 1;
	if(sum >= k) return query(tree[x].r,tree[y].r,mid + 1,r,k); //倘若右边已经有k个那就直接往右边递归
	else return query(tree[x].l,tree[y].l,l,mid,k - sum) + tree[tree[y].r].s - tree[tree[x].r].s; //倘若右边不够 那么就先把右边的值 加上 然后再去递归左边
}

trie树
假设需要存入 abcdef abcff abcki 等字符串
存1.那我们先从顶点查看是否有a这个点 若没有那么从a点加一个新节点b 然后加入cdef
存2.我们先看有a a里有b b里有 c c没有f 所以存入 f 然后f存入f

#include<iostream>
using namespace std;
const int maxn = 100010;
int son[maxn][26],cnt[maxn],idx;
void insert(char ch[]){
    
    
    int p = 0;
    for(int i = 0; ch[i]; i++){
    
    
        int s = ch[i] - 'a';
        if(!son[p][s]) son[p][s] = ++idx;
        p = son[p][s];
    }
    cnt[p]++; //查看这个字符串出现了多少次
    return;
}
int query(char ch[]){
    
    
    int p = 0;
    for(int i = 0; ch[i]; i++){
    
    
        int s = ch[i] - 'a';
        if(!son[p][s]) return 0;
        p = son[p][s];
    }
    return cnt[p];
}

int main(){
    
    
    int t;
    cin >> t;
    char a, ch[maxn];
    while(t--){
    
    
        cin >> a >> ch;
        if(a == 'I'){
    
    
            insert(ch);
        }else{
    
    
            cout << query(ch) << endl;
        }
    }
    
    return 0;
}

可持久化trie树
用来查询区间 异或 与 x 的最大值
首先我们想想怎么找到与x异或的最大值 那么就是 利用trie树
每次查看是否有 与 二进制x 当前选择的 i 相反的 若有 那就选择那一条 进行 i + 1的查询
利用这个思想 我们查看区间的话 我们只需要 利用可持久化trie 树来限制 右边 因为 他是利用n棵 tire树 进行 存储的 所以 查询到R 只需要利用第 R棵trie树 我们已经限制了 R 怎么限制 L呢 我们只需要在查询的时候看 这个串 的最大出现的地方 是否大于L 即可 所以我们只需要每一次将 不同于上一棵树 的地方 新键一个 新的trie 树 赋值 就好了

#include<iostream>

using namespace std;

const int N = 6e5 + 10,M = N * 24;

int tr[M][2],a[N],root[M],max_id[M];
int tot;
void insert(int x,int k,int p,int q){
    
    
	if(k == -1){
    
    
		max_id[q] = x;
		return;
	}
	int v = (a[x] >> k) & 1;
	if(p) tr[q][v ^ 1] = tr[p][v ^ 1]; //如果有的话 那么另一半相同 
	tr[q][v] = ++tot; //创立一个新的 为了产生一个新的 max_id 为 x 得 串 
	insert(x,k - 1,tr[p][v],tr[q][v]);
	max_id[q] = max(max_id[tr[q][0]],max_id[tr[q][1]]);
}

int query(int root,int C,int L){
    
    
	int p = root;
	for(int i = 23; i >= 0; i--){
    
    
		int v = C >> i & 1;
		if(max_id[tr[p][v ^ 1]] >= L) p = tr[p][v ^ 1];//若最大值大于L则进入这个 否则被迫进入另一边 
		else p = tr[p][v];
	}
	return C ^ a[max_id[p]];
}

int main(){
    
    
	int n,m;
	scanf("%d%d",&n,&m);
	root[0] = ++ tot;
	max_id[0] = -1;
	insert(0,23,0,root[0]);
	
	for(int i = 1; i <= n; i++){
    
    
		int x; 
		scanf("%d",&x);
		a[i] = a[i - 1] ^ x;
		root[i] = ++tot;
		insert(i,23,root[i - 1],root[i]);
	}
	
	char op[3];
	int l,r,x;
	while(m--){
    
    
		scanf("%s",op);
		if(*op == 'A'){
    
    
			scanf("%d",&x);
			++n;
			a[n] = a[n - 1] ^ x;
			root[n] = ++tot;
			insert(n,23,root[n - 1],root[n]);
		}else{
    
    
			scanf("%d%d%d",&l,&r,&x);
			printf("%d\n",query(root[r - 1],a[n] ^ x,l - 1));
		}
	}
	
	
	
	
	
	return 0;
} 

散列表
存一个单链表

#include<iostream>
#include<cstring>
using namespace std;
const int N = 100003;
int head[N],last[N],to[N],cnt;
void insert(int x){
    
    
    int k  = (x % N + N) % N;
    to[cnt] = x;
    last[cnt] = head[k];
    head[k] = cnt++;
}
bool query(int x){
    
    
    int k = (x % N + N) % N;
    for(int i = head[k]; i != -1; i = last[i]){
    
    
        if(to[i] == x) return true;
    }
    return false;
}
int main(){
    
    
    int n;
    cin >> n;
    char a[2];int x;
    memset(head,-1,sizeof head);
    for(int i = 1; i <= n; i++){
    
    
        scanf("%s%d",a,&x);
        if(a[0] == 'I') insert(x);
        else{
    
    
            if(query(x)) cout << "Yes" << endl;
            else cout << "No" << endl;
        }
    }
    return 0;
}


字符串哈希

#include<iostream>
using namespace std;
typedef unsigned long long ull;
const int maxn = 100010;
char ch[maxn];
ull h[maxn],p[maxn]; //ull 溢出自动mod 2^64 这个就是关键
int main(){
    
    
    int n,m;
    scanf("%d%d%s",&n,&m,ch + 1);
    int s = 131;
    p[0] = 1;
    for(int i = 1; i <= n; i++){
    
    
        p[i] = p[i - 1] * s;
        h[i] = h[i - 1] * s + ch[i];
    }
    int x1,y1,x2,y2;
    for(int i = 1; i <= m; i++){
    
    
        cin >> x1 >> y1 >> x2 >> y2;
        if(h[y1] - h[x1 - 1] * p[y1 - x1 + 1] == h[y2] - h[x2 - 1] * p[y2 - x2 + 1]){
    
    
            cout << "Yes" << endl;
        }else cout << "No" << endl;
    }
    return 0;
}

模拟堆
模拟堆模板
在这里插入图片描述

#include<iostream>
using namespace std;
const int maxn = 1e5 + 5;
string ch;int cnt = 0,h[maxn],ps1[maxn],ps2[maxn],s = 0;//ps1记录 第i个插入的数的位置,ps2记录 这个位置的数是第几个插入的;
void swap_h(int x,int y){
    
    
    swap(h[x],h[y]);
    swap(ps2[x],ps2[y]);
    swap(ps1[ps2[x]],ps1[ps2[y]]);
}
void down(int x){
    
    
    int t = x;
    if(x * 2 <= cnt && h[t] > h[x * 2]) t = x * 2;
    if(x * 2 + 1 <= cnt && h[t] > h[x * 2 + 1]) t = x * 2 + 1;
    if(t != x){
    
    
        swap_h(x,t);
        down(t);
    }
}
void up(int x){
    
    
    if(x / 2 > 0 && h[x / 2] > h[x]){
    
    
        swap_h(x,x / 2);
        up(x >> 1);
    }
}
void insert(int x){
    
    
    s++;
    h[++cnt] = x;
    ps1[s] = cnt;
    ps2[cnt] = s;
    up(cnt);
}
void output(){
    
    
    cout << h[1] << endl;
}
void change(int x,int y){
    
    
    h[ps1[x]] = y;
    up(ps1[x]);
    down(ps1[x]);
}
int main(){
    
    
    int n;
    cin >> n;
    int x,y;
    for(int i = 1; i <= n; i++){
    
    
        cin >> ch;
        if(ch == "I"){
    
    
            cin >> x;
            insert(x);
        }else if(ch == "PM"){
    
    
            output();
        }else if(ch == "DM"){
    
    
            swap_h(1,cnt);
            cnt--;
            down(1);
        }else if(ch == "D"){
    
    
            cin >> x;
            int u = ps1[x];
            swap_h(u,cnt);
            cnt--;
            up(u);
            down(u);
        }else{
    
    
            cin >> x >> y;
            change(x,y);
        }
    }
    return 0;
}

莫队模板

#include <stdio.h>
#include<cmath>
#include <vector>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#define ll long long
#define chushi(a, b) memset(a, b, sizeof(a))
#define endl "\n"
const double eps = 1e-8;
const ll INF=0x3f3f3f3f;
const int mod=1e9 + 7;
const int maxn = 3e5 + 5;
const int N=1005;
using namespace std;

int a[maxn], ANS = 0, tm[maxn], cnt[maxn], ans[maxn];

typedef struct Node{
    
    
	int l, r;
	int id;
} node;

int sqt;
bool cmp(node A, node B){
    
    
	if(A.l/sqt == B.l/sqt) return A.l/sqt%2 == 1 ? A.r < B.r : A.r > B.r;
	else return A.l/sqt < B.l/sqt;
}

node q[maxn];

void add(int num){
    
    
	--cnt[tm[num]]; 
	ANS = max(ANS, ++tm[num]); 
	++cnt[tm[num]];
}

void subd(int num){
    
    
	--cnt[tm[num]]; 
	if(ANS == tm[num] && cnt[tm[num]] == 0) ANS--;
	--tm[num], ++cnt[tm[num]];
}

int main() {
    
    

	int n, m;
	scanf("%d %d", &n, &m);
	sqt = sqrt(n);
	for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
	for(int i = 1; i <= m; i++) scanf("%d %d", &q[i].l, &q[i].r);
	for(int i = 1; i <= m; i++) q[i].id = i;
	
	sort(q+1, q+1+m, cmp);
	
	for(int l = 1, r = 0, i = 1; i <= m; i++){
    
    
		int ln = q[i].l, rn = q[i].r;
		while(l < ln) subd(a[l++]);
		while(l > ln) add(a[--l]);
		while(r < rn) add(a[++r]);
		while(r > rn) subd(a[r--]);
		ans[q[i].id] = 1;
		if(ANS > (rn-ln+2)/2) ans[q[i].id] = 2 * ANS - (rn-ln+1);
	}
	
	for(int i = 1; i <= m; i++) printf("%d\n", ans[i]);

	return 0;
}

双向链表

ll to[N],last[N],w[N];
void addl(int x,int y,ll z){
    
    
    to[last[y]] = x;
    last[x] = last[y];
    to[x] = y;
    w[x] = z;
    last[y] = x;
}

void addr(int x,int y,ll z){
    
    
    last[to[y]] = x;
    to[x] = to[y];
    to[y] = x;
    w[x] = z;
    last[x] = y;
}

void del(int x){
    
    
    to[last[x]] = to[x];
    last[to[x]] = last[x];
    to[x] = 0;
    w[x] = 0;
    last[x] = 0;
}

树链剖分模板
树链剖分 就是将树上的节点利用时间戳 来将子树或者链 变成区间的形式去实现 加减 有几个重要的概念 1.重儿子 是每个点的儿子中 子树大小最大的 (若有多个儿子子树大小相同 且 最大 那么就随便选) 2.轻儿子 除了重儿子 所有的点 3.重边就是重儿子和他父亲节点连的边 4.重链由多个重边组成的链
fa[N] 保存每个点的父亲节点
top[N]保存每个重链的顶点
id[N]保存每个点的时间戳
depth[N]保存每个点的深度
son[N]保存每个点的重儿子

在这里插入图片描述
首先dfs遍历一遍找出每个子树的大小和重儿子 和深度
然后再跑第二个dfs 知道每条重链的顶
首先我们可以想到 因为 当前点的重儿子 肯定是和 当前点的重链的顶是一样的 为了保证重链的边是连续的所以我们首先跑重儿子 其次再跑轻儿子 这样就可以利用 类似lca的思想 每次查询当前top[u]和top[v]是否相同 如果相同的话 那么他们的点在同一条重链上就可以退出了 若不在同一条重链上首先判断哪个点的depth小 小的那位往上跑 并且将这一次的重链上的边都修改掉
图中的红线即是重边 2 3 4 7 是重儿子 若想让 4 7的路径上 都加上1的话
4的顶是1 7的顶是6 所以把7 - 6先修改掉 然后 v = fa[top[v]]
然后4的顶是 1 1的顶是 1 所以他们的链相同 此时depth[1]小于depth[4]所以修改 (1,4)就好了

#include<iostream>
#include<cstring>

using namespace std;

const int N = 1e5 + 10,M = 2e5 + 10;

typedef long long ll;

int head[N],to[M],last[M],w[M],cnt;
void add(int a,int b){
    
    
    to[++cnt] = b;
    last[cnt] = head[a];
    head[a] = cnt;
}

struct node{
    
    
    int l,r;ll add,sum;
}tree[N * 4];

int sz[N],nw[N];
int top[N],id[N],depth[N],times,fa[N],son[N];
void dfs1(int x,int father){
    
    
    sz[x] = 1;
    depth[x] = depth[father] + 1;
    fa[x] = father;
    for(int i = head[x]; i != -1; i = last[i]){
    
    
        int j = to[i];
        if(j == father) continue;
        dfs1(j,x);
        sz[x] += sz[j];
        if(sz[son[x]] < sz[j]){
    
    
            son[x] = j;
        }
    }
}

void dfs2(int x,int tp){
    
    
    top[x] = tp;
    id[x] = ++times;
    nw[times] = w[x];
    if(!son[x]) return;
    dfs2(son[x],tp);
    for(int i = head[x]; i != -1; i = last[i]){
    
    
        int j = to[i];
        if(j == fa[x] || j == son[x]) continue;
        dfs2(j,j);
    }
}

void pushup(int p){
    
    
    tree[p].sum = tree[p * 2].sum + tree[p * 2 + 1].sum;
}

void pushdown(int p){
    
    
    tree[p * 2].add += tree[p].add;
    tree[p * 2].sum += tree[p].add * (tree[p * 2].r - tree[p * 2].l + 1);
    tree[p * 2 + 1].add += tree[p].add;
    tree[p * 2 + 1].sum += tree[p].add * (tree[p * 2 + 1].r - tree[p * 2 + 1].l + 1);
    tree[p].add = 0;
}

void build(int l,int r,int p){
    
    
    tree[p].l = l,tree[p].r = r,tree[p].sum = nw[r];
    if(l == r) return;
    int mid = l + r >> 1;
    build(l,mid,p * 2);
    build(mid + 1,r,p * 2 + 1);
    pushup(p);
}

void update(int l,int r,int p,int k){
    
    
    if(tree[p].l >= l && tree[p].r <= r){
    
    
        tree[p].add += k;
        tree[p].sum += (tree[p].r - tree[p].l + 1) * k;
        return;
    }
    pushdown(p);
    int mid = tree[p].l + tree[p].r >> 1;
    if(l <= mid) update(l,r,p * 2,k);
    if(mid < r) update(l,r,p * 2 + 1,k);
    pushup(p);
}

ll query(int l,int r,int p){
    
    
    if(tree[p].l >= l && tree[p].r <= r){
    
    
        return tree[p].sum;
    }
    pushdown(p);
    int mid = tree[p].l + tree[p].r >> 1;
    ll sum = 0;
    if(l <= mid) sum = query(l,r,p * 2);
    if(mid < r) sum += query(l,r,p * 2 + 1);
    return sum;
}

void update_path(int u,int v,int k){
    
    
    while(top[u] != top[v]){
    
    
        if(depth[top[u]] < depth[top[v]]) swap(u,v);
        update(id[top[u]],id[u],1,k);
        u = fa[top[u]];
    }
    if(depth[u] > depth[v]) swap(u,v);
    update(id[u],id[v],1,k);
}

ll query_path(int u,int v){
    
    
    ll sum = 0;
    while(top[u] != top[v]){
    
    
        if(depth[top[u]] < depth[top[v]]) swap(u,v);
        sum += query(id[top[u]],id[u],1);
        u = fa[top[u]];
    }
    if(depth[u] > depth[v]) swap(u,v);
    sum += query(id[u],id[v],1);
    return sum;
}

void update_tree(int u,int k){
    
    
    update(id[u],id[u] + sz[u] - 1,1,k);
}

ll query_tree(int u){
    
    
    return query(id[u],id[u] + sz[u] - 1,1);
}

int main(){
    
    
    int n;
    cin >> n;
    for(int i = 1; i <= n; i++){
    
    
        scanf("%d",&w[i]);
    }

    memset(head,-1,sizeof head);
    for(int i = 1; i <= n - 1; i++){
    
    
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }

    dfs1(1,0);
    dfs2(1,1);
    build(1,n,1);

    int m;
    cin >> m;
    while(m--){
    
    
        int op,u,v,k;
        scanf("%d",&op);
        if(op == 1){
    
    
            scanf("%d%d%d",&u,&v,&k);
            update_path(u,v,k);
        }else if(op == 2){
    
    
            scanf("%d%d",&u,&k);
            update_tree(u,k);
        }else if(op == 3){
    
    
            scanf("%d%d",&u,&v);
            printf("%lld\n",query_path(u,v));
        }else{
    
    
            scanf("%d",&u);
            printf("%lld\n",query_tree(u));
        }
    }




    return 0;
}

左偏树
左偏树概念为左右子树都比根结点大 左边子树大小比右边子树大小大 即根节点到非空节点的大小为右边子树的最浅 + 1
在这里插入图片描述

#include<iostream>
#include<cstring>

using namespace std;

const int N = 2e5 + 10;

struct node{
    
    
    int v,dis,l,r;
}tree[N];

int fa[N],idx;

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

bool cmp(int x,int y){
    
    
    if(tree[x].v == tree[y].v){
    
    
        return x < y;
    }
    return tree[x].v < tree[y].v;
}

int merge(int x,int y){
    
    
    if(!x || !y) return x + y;
    if(cmp(y,x)) swap(x,y);
    tree[x].r = merge(tree[x].r,y);
    if(tree[tree[x].r].dis > tree[tree[x].l].dis) swap(tree[x].l,tree[x].r);
    tree[x].dis = tree[tree[x].r].dis + 1;
    return x;
}

int main(){
    
    
    int t;
    cin >> t;
    tree[0].v = 2e9;
    while(t--){
    
    
        int op,x,y;
        scanf("%d%d",&op,&x);

        if(op == 1){
    
    
            tree[++idx].v = x;
            tree[idx].dis = 1;
            fa[idx] = idx;
        }else if(op == 2){
    
    
            scanf("%d",&y);
            int fx = get(x),fy = get(y);
            if(fx != fy){
    
    
                if(cmp(fy,fx)) swap(fx,fy);
                fa[fy] = fx;
                merge(fx,fy);
            }
        }else if(op == 3){
    
    
            cout << tree[get(x)].v << endl;
        }else{
    
    
            int fx = get(x);
            if(cmp(tree[fx].r,tree[fx].l)) swap(tree[fx].r,tree[fx].l);
            fa[fx] = tree[fx].l;
            fa[tree[fx].l] = tree[fx].l;
            merge(tree[fx].l,tree[fx].r);
        }
    }





    return 0;
}

克鲁斯卡儿重构树
顾名思义 使用最小生成树来建成的树 每次选择边权最小的边 往里面加 每两个连通块联通至新加的一个点 让他们的祖先连向 这个点 那么 每次都会让边权更大的边 体现出的那个点 连向 边权小的点
在这里插入图片描述

for(int i = 1; i <= m; i++){
    
    
        int x = get(edge[i].x),y = get(edge[i].y);
        if(x != y){
    
    
            fa[x] = fa[y] = ++ext;
            v[ext] = edge[i].w;
            add(ext,x);
            add(ext,y);
        }
    }

猜你喜欢

转载自blog.csdn.net/qqqingyi/article/details/115358615
今日推荐