数据结构---可持久化线段树

写在前面

Q:什么时候需要可持久化?
A:想建一车线段树但是建不下的时候。 ----Luckyblock

感觉Lb已经写的很好了,就不需要我再胡扯了/cy
扔个传送门咕咕了
「笔记」可持久化线段树

可持久化数组/动态开点线段树

P3919 【模板】可持久化线段树 1(可持久化数组)

Code

/*
Work by: Suzt_ilymics
Knowledge: ??
Time: O(??)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 1e6+5;
const int INF = 1e9+7;
const int mod = 1e9+7;

int n, m;
int a[MAXN];

int read(){
	int s = 0, f = 0;
	char ch = getchar();
	while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
	while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
	return f ? -s : s;
}

namespace Seg{
	#define ls lson[now_]
	#define rs rson[now_]
	#define mid ((L + R) >> 1)
	int node_num = 0, root[MAXN];
	int val[MAXN << 6], lson[MAXN << 6], rson[MAXN << 6];
	void Insert(int &now_, int L, int R){
		now_ = ++ node_num;
		if(L == R){
			val[now_] = a[L];
			return ;
		}
		Insert(ls, L, mid);
		Insert(rs, mid + 1, R);
	}
	void Change(int &now_, int pre_, int L, int R, int pos_, int val_){
		now_ = ++ node_num;
		if(L == R){
			val[now_] = val_;
			return ;
		}
		ls = lson[pre_];
		rs = rson[pre_];
		if(pos_ <= mid) Change(ls, lson[pre_], L, mid, pos_, val_);
		else Change(rs, rson[pre_], mid + 1, R, pos_, val_);
	}
	int Query(int now_, int L, int R, int pos_){
		if(L == R) return val[now_];
		if(pos_ <= mid) return Query(ls, L, mid, pos_);
		else return Query(rs, mid + 1, R, pos_);
	}
	#undef ls
	#undef rs
	#undef mid
}

int main()
{
	n = read(), m = read();
	for(int i = 1; i <= n; ++i) a[i] = read();
	Seg::Insert(Seg::root[0], 1, n);
	for(int i = 1, pre, opt, pos, val; i <= m; ++i){
		pre = read(), opt = read(), pos = read();
		if(opt == 1){
			val = read();
			Seg::Change(Seg::root[i], Seg::root[pre], 1, n, pos, val);
		}
		else{
			printf("%d\n", Seg::Query(Seg::root[pre], 1, n, pos));
			Seg::root[i] = Seg::root[pre];
		}
	}
	return 0;
}

可持久化线段树/主席树

【模板】可持久化线段树 2(主席树)

/*
Work by: Suzt_ilymics
Knowledge: ??
Time: O(??)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 2e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;

int n, m;
int a[MAXN], data[MAXN], data_num = 0;
int root[MAXN];

int read(){
	int s = 0, f = 0;
	char ch = getchar();
	while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
	while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
	return f ? -s : s;
}

void init(){
	n = read(), m = read();
	for(int i = 1; i <= n; ++i) a[i] = data[i] = read();
	sort(data + 1, data + n + 1);
	data[0] = -0x3f3f3f3f;
	for(int i = 1; i <= n; ++i) if(data[i] != data[i - 1]) data[++data_num] = data[i];
	for(int i = 1; i <= n; ++i){
		a[i] = lower_bound(data + 1, data + data_num + 1, a[i]) - data; 
	}
}

namespace Hjt{
	#define ls lson[now_]
	#define rs rson[now_]
	#define mid ((L + R) >> 1)
	int node_num, lson[MAXN << 5], rson[MAXN << 5], siz[MAXN << 5];
	void Insert(int &now_, int pre_, int L, int R, int val_){
		now_ = ++ node_num;
		siz[now_] = siz[pre_] + 1;
		lson[now_] = lson[pre_], rson[now_] = rson[pre_];
		if(L == R) return ;
		if(val_ <= mid){
			Insert(ls, lson[pre_], L, mid, val_);
		}
		else{
			Insert(rs, rson[pre_], mid + 1, R, val_);
		}	
	}
	int Query(int r, int l, int L, int R, int k){
		if(L == R) return L;
		int sz = siz[lson[r]] - siz[lson[l]];
		if(k <= sz){
			return Query(lson[r], lson[l], L, mid, k);
		}
		else{
			return Query(rson[r], rson[l], mid + 1, R, k - sz);
		}
	}
	#undef ls
	#undef rs
	#undef mid
}

int main()
{
	init();
	for(int i = 1; i <= n; ++i) Hjt::Insert(root[i], root[i - 1], 1, data_num, a[i]);
	for(int i = 1, l, r, k; i <= m; ++i){
		l = read(), r = read(), k = read();
		printf("%d\n", data[Hjt::Query(root[r], root[l - 1], 1, data_num, k)]);
	}
	return 0;
}

带修主席树

P2617 Dynamic Rankings

/*
Work by: Suzt_ilymics
Knowledge: ??
Time: O(??)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 1e6+5;
const int INF = 1e9+7;
const int mod = 1e9+7;

struct node{
	int opt, l, r, k, pos, val;
}q[MAXN];

int n, m;
int a[MAXN], data[MAXN << 2], data_num;
int num[MAXN << 2];
vector<int> tmp1, tmp2;

int read(){
	int s = 0, f = 0;
	char ch = getchar();
	while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
	while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
	return f ? -s : s;
}

namespace Hjt{//主席树(可能算基操? 
	#define ls lson[now_]
	#define rs rson[now_]
	#define mid ((L + R) >> 1)
	int node_num = 0, root[MAXN << 2], siz[MAXN << 5], lson[MAXN << 5], rson[MAXN << 5];
	void Modify(int &now_, int L, int R, int pos_, int val_){
		if(!now_) now_ = ++node_num;
		siz[now_] += val_;
		if(L == R) return ;
		if(pos_ <= mid) Modify(ls, L, mid, pos_, val_);
		else Modify(rs, mid + 1, R, pos_, val_);
	}
	int Query(int L, int R, int k){
		if(L == R) return L;
		int sizel = 0;
		for(int i = 0; i < tmp1.size(); ++i) sizel -= siz[lson[tmp1[i]]];
		for(int i = 0; i < tmp2.size(); ++i) sizel += siz[lson[tmp2[i]]];
		
		if(k <= sizel){
			for(int i = 0; i < tmp1.size(); ++i) tmp1[i] = lson[tmp1[i]];
			for(int i = 0; i < tmp2.size(); ++i) tmp2[i] = lson[tmp2[i]];
			return Query(L, mid, k);
		}
		else{
			for(int i = 0; i < tmp1.size(); ++i) tmp1[i] = rson[tmp1[i]];
			for(int i = 0; i < tmp2.size(); ++i) tmp2[i] = rson[tmp2[i]];
			return Query(mid + 1, R, k - sizel);
		}
	}
}

namespace Bit{//用树状数组来平衡复杂度 
	int lowbit(int x){ return x & (-x); }
	void Add(int pos_, int val_){
		int p = lower_bound(data + 1, data + data_num + 1, num[pos_]) - data;
		for(int i = pos_; i <= n; i += lowbit(i)){
			Hjt::Modify(Hjt::root[i], 1, data_num, p, val_);
		}
	}
	int Query(int l, int r, int k){
		tmp1.clear(), tmp2.clear();
		for(int i = l - 1; i; i -= lowbit(i)) tmp1.push_back(Hjt::root[i]);
		for(int i = r; i; i -= lowbit(i)) tmp2.push_back(Hjt::root[i]);
		return Hjt::Query(1, data_num, k);
	}
}

void init(){
	n =read(), m = read();
	for(int i = 1; i <= n; ++i) data[++ data_num] = num[i] = read();
	for(int i = 1; i <= m; ++i){
		char ch; cin >> ch;
		q[i].opt += (ch == 'Q');
		if(q[i].opt){
			q[i].l = read(), q[i].r = read(), q[i].k = read();
		}else{
			q[i].pos = read(), q[i].val = data[++ data_num] = read();
		}
	}
	sort(data + 1, data + data_num + 1);
	data_num = unique(data + 1, data + data_num + 1) - data - 1;
	for(int i = 1; i <= n; ++i) Bit::Add(i, 1);
}

int main()
{
	init();
	for(int i = 1; i <= m; ++i){
		if(q[i].opt){
			printf("%d\n", data[Bit::Query(q[i].l, q[i].r, q[i].k)]);
		}
		else{
			Bit::Add(q[i].pos, -1);
			num[q[i].pos] = q[i].val;
			Bit::Add(q[i].pos, 1);
		}
	}
	return 0;
}

例题

用来求逆序对

[CTSC2018]混合果汁
Solution:把美味度当做时间戳

[SDOI2014]旅行
Solution:发现所有点只信奉一个宗教,对每个宗教都开个线段树或者计数的话难以维护,考虑把宗教当做时间戳动态开点,外套一个树剖即可
Code:LojCode

posted @ 2021-02-28 20:12  Suzt_ilymtics  阅读(122)  评论(1编辑  收藏  举报