数据结构---可持久化线段树
写在前面
Q:什么时候需要可持久化?
A:想建一车线段树但是建不下的时候。 ----Luckyblock
感觉Lb已经写的很好了,就不需要我再胡扯了/cy
扔个传送门咕咕了
「笔记」可持久化线段树
可持久化数组/动态开点线段树
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;
}
可持久化线段树/主席树
/*
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;
}
带修主席树
/*
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