主席树
前言
这玩意哪儿都好,好想,好用,就是不好码
它太长了!
前置知识:线段树
正题
主席树——望文生义:主席发明的树
主席树——就是很多个线段树(个人理解,如下图:)
当然,它最开始就只有一棵线段树(蓝色)
如果你要单点修改最右边的一个点,那么这个点到根的一条链的状态就会发生改变,而其它点不会,如果我们为此再建一棵线段树,空间和时间都受不了
但是我们发现只有一条链的状态改变了,其它的都没有变,所以我们可以只建这条链,如图红色部分,其它的就直接把儿子节点的编号赋为之前的线段树(蓝色)
同理,要改左边的一个点也一样(绿色)
好了,我们现在了解了主席树了,来一道板题练练手:
习题一
具体的实现看代码
下面是丑陋的代码:
//12252024832524
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 10005;
const int MAXQ = 100005;
int n,Q,tot,edition;
int pd,k;
int changed,val;
int ql,qr;
int root[MAXQ];
struct tree
{
int l,r,MAX;
}t[(MAXN << 2) + MAXQ * 17];
int Read()
{
int x = 0,f = 1;char c = getchar();
while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
return x * f;
}
void Put(int x)
{
if(x > 9)
Put(x/10);
putchar(x%10^48);
}
int Max(int x,int y){return x > y ? x : y;}
int Min(int x,int y){return x < y ? x : y;}
void Build(int x,int l,int r)
{
if(l == r)
{
t[x].MAX = Read();
return;
}
t[x].l = ++tot;
t[x].r = ++tot;
int mid = (l+r) >> 1;
Build(t[x].l,l,mid);
Build(t[x].r,mid+1,r);
t[x].MAX = Max(t[t[x].l].MAX,t[t[x].r].MAX);
}
int Query(int x,int l,int r)
{
if(l > qr || r < ql)
return -0x3f3f3f3f;
if(l >= ql && r <= qr)
return t[x].MAX;
int mid = (l+r) >> 1;
return Max(Query(t[x].l,l,mid),Query(t[x].r,mid+1,r));
}
void Add(int x,int l,int r,int kx)
{
if(l == r)
{
t[x].MAX = val;
return;
}
int mid = (l+r) >> 1;
if(changed <= mid)//左
{
t[x].l = ++tot;
t[x].r = t[kx].r;
Add(tot,l,mid,t[kx].l);
}
else
{
t[x].l = t[kx].l;
t[x].r = ++tot;
Add(tot,mid+1,r,t[kx].r);
}
t[x].MAX = Max(t[t[x].l].MAX,t[t[x].r].MAX);
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n = Read();
Q = Read();
root[++edition] = ++tot;
Build(root[1],1,n);
for(; Q ;-- Q)
{
pd = Read();
k = Read();
if(!pd)
{
ql = Read();
qr = Read();
Put(Query(root[k],1,n));
putchar('\n');
}
else
{
changed = Read();
val = Read();
root[++edition] = ++tot;
Add(tot,1,n,root[k]);
}
}
return 0;
}
习题二
注意题目中的离散化( \(ppl\) 为离散化数组)
//12252024832524
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 200005;
int n,Q,tot,edition;
int k,changed;
int ql,qr;
int root[MAXN],ppl[MAXN];
struct node
{
int a,ID;
bool operator < (const node &px)const
{
return a < px.a;
}
}s[MAXN];
struct tree
{
int lson,rson,cnt;
}t[(MAXN << 2) + MAXN * 18];
int Read()
{
int x = 0,f = 1;char c = getchar();
while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
return x * f;
}
void Put(int x)
{
if(x > 9)
Put(x/10);
putchar(x%10^48);
}
int Max(int x,int y){return x > y ? x : y;}
int Min(int x,int y){return x < y ? x : y;}
void Build(int x,int l,int r)
{
if(l == r)
{
t[x].cnt = 0;
return;
}
t[x].lson = ++tot;
t[x].rson = ++tot;
int mid = (l+r) >> 1;
Build(t[x].lson,l,mid);
Build(t[x].rson,mid+1,r);
}
int Query(int x1,int x2,int l,int r)//x2 - x1
{
// printf("Q : %d %d %d %d %d\n",l,r,k,t[x2].cnt,t[x1].cnt);
if(l == r)
return l;
int dz = t[t[x2].lson].cnt - t[t[x1].lson].cnt;
int mid = (l+r) >> 1;
if(k <= dz)
return Query(t[x1].lson,t[x2].lson,l,mid);
k -= dz;
return Query(t[x1].rson,t[x2].rson,mid+1,r);
}
void Add(int x,int l,int r,int kx)
{
if(l == r)
{
t[x].cnt = 1;
return;
}
int mid = (l+r) >> 1;
if(changed <= mid)//左
{
t[x].lson = ++tot;
t[x].rson = t[kx].rson;
Add(tot,l,mid,t[kx].lson);
}
else
{
t[x].lson = t[kx].lson;
t[x].rson = ++tot;
Add(tot,mid+1,r,t[kx].rson);
}
t[x].cnt = t[t[x].lson].cnt + t[t[x].rson].cnt;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n = Read();
Q = Read();
Build(root[0],1,n);
for(int i = 1;i <= n;++ i)
{
s[i].a = Read();
s[i].ID = i;
}
sort(s+1,s+n+1);
for(int i = 1;i <= n;++ i)
ppl[s[i].ID] = i;
for(int i = 1;i <= n;++ i)
{
root[++edition] = ++tot;
changed = ppl[i];
Add(tot,1,n,root[edition-1]);
}
for(; Q ;-- Q)
{
ql = Read();
qr = Read();
k = Read();
Put(s[Query(root[ql-1],root[qr],1,n)].a);
putchar('\n');
}
return 0;
}