主席树

前言

这玩意哪儿都好,好想,好用,就是不好码

它太了!

前置知识:线段树

正题

主席树——望文生义:主席发明的树

主席树——就是很多个线段树(个人理解,如下图:)

当然,它最开始就只有一棵线段树(蓝色)

如果你要单点修改最右边的一个点,那么这个点到根的一条链的状态就会发生改变,而其它点不会,如果我们为此再建一棵线段树,空间和时间都受不了

但是我们发现只有一条链的状态改变了,其它的都没有变,所以我们可以只建这条链,如图红色部分,其它的就直接把儿子节点的编号赋为之前的线段树(蓝色)

同理,要改左边的一个点也一样(绿色)

好了,我们现在了解了主席树了,来一道板题练练手:

习题一

USOJ3041

具体的实现看代码

下面是丑陋的代码:

//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;
}

习题二

可持久化线段树 1(主席树)(洛谷)

注意题目中的离散化( \(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;
}
posted @ 2020-07-28 16:30  皮皮刘  阅读(104)  评论(0编辑  收藏  举报