可持久化数据结构

前言

刚学完这个算法,说是提高算法里的,要不是我去看大纲我不知道还得研究多久......上写写的清清楚楚 NOI 级算法。。。。
所以就浅谈一下。

正文

什么是可持久化数据结构呢?

简单一点来说,就是能支持访问以往某个版本的数据的数据结构,当然我的总结并没有那么贴切……

我们以这样一个事来引入吧!记得上学期的时候上数学课学统计,听板栗在上面讲课,我在下面突然想到,能否写一种数据结构,能够快速访问每一个给定区间的中位数是多少?

考虑最暴力的做法就是每次sort取中间,但这样显然太慢了,然后如果在每个区间都建一棵权值线段树来维护,那空间又太大了。

所以我们引入一个新的强势数据结构——可持久化线段树(主席树)来解决这个问题!

为啥叫主席树呢?传说发明这种数据结构的神犇黄嘉泰,因为他在考试的时候不会写归并树就写了主席树这种东西替代归并树,并成功让广大OIer使用了这种数据结构,把归并树扔进了垃圾箱。因为他的名字缩写HJT也是当时chairman的名字缩写,故称为主席树。

言归正传。其实主席树相当于在每一个节点维护了一棵线段树(不是真的建了出来!)主席树节点中维护的值,是1-i之间这个区间内出现了数的次数。然后当我们查询的时候,就是利用到了前缀和的思想。

以一个建树过程为例:

然后开始插入,把经过的每一个节点的v++。

这样一直插入,直到最后把所有数插入完毕。

好的,那我们现在要查询。怎么查询呢?既然要查询[2,5]区间内第3大的数,那么就先把第一棵和第5棵线段树拿出来。

这时候我们惊奇的发现,对于每一个区间,我们用第5棵树中该区间的权值减去第一棵树中改区间的权值,发现delta就是在该范围的数在[2,5]中出现的次数!

这里其实是用到了前缀和的思想。仔细思考一下即可知。

这样的话我们就能在这棵delta树(这是沿用了兔哥的叫法)上进行k大值查询。只要像正常的权值线段树一样,如果当前数的排名大于左子树大小,就在右子树中找,否则在左子树中找就可以了。

不过实际上这棵树并不是这个样子的,上面的样子只是比较好理解,我们实际在操作的时候每修改一次就要新建一条链,不过我们只处理和本次修改操作有关的,无关的直接和原树共用即可。

然后我们就完成了。

模板

【模板】可持久化线段树

#include<bits/stdc++.h>
using namespace std;

const int oo=1e9+7;
const int maxn=2e5+15;
int n,m,cnt;
it ans[maxn],tree[maxn]; 
struct NODE
{
	int x;int y;int k;
	int id;int type;
}q[maxn<<1],q1[maxn<<1],q2[maxn<<1];
void add(int x,int y)
{
	while (x<=n)
	{
		tree[x]+=y;
		x+=x&(-x);
	}
}
int sum(int x)
{
	int ans=0;
	while (x)
	{
		ans+=tree[x];
		x-=x&(-x);
	}
	return ans;
}
void solve(int ql,int qr,int l,int r)
{
	if (ql>qr) return;
	if (l==r){
		for (int i=ql;i<=qr;i++)
		if (q[i].type==2) ans[q[i].id]=l;
		return;
	}
	int mid=(l+r)>>1;
	int p1=0,p2=0;
	for (int i=ql;i<=qr;i++)
    if (q[i].type==1){
    	if (q[i].x<=mid){
    		add(q[i].id,1);
    		q1[++p1]=q[i];
		}
		else q2[++p2]=q[i];
	}
	else {
		int res=sum(q[i].y)-sum(q[i].x-1);
		if (res>=q[i].k) q1[++p1]=q[i];
		else {
			q[i].k-=res;
			q2[++p2]=q[i];
		}
	}
	for (int i=1;i<=p1;i++) if (q1[i].type==1) add(q1[i].id,-1);
	for (int i=1;i<=p1;i++)
	q[i+ql-1]=q1[i];
	for (int i=1;i<=p2;i++)
	q[i+ql+p1-1]=q2[i];
	solve(ql,ql+p1-1,l,mid);
	solve(ql+p1,qr,mid+1,r);
}
int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	{
		int x;
		scanf("%d",&x);
		q[++cnt]=(NODE){x,1,oo,i,1};
	}
	for (int i=1;i<=m;i++)
	{
		int x,y,k;
		scanf("%d%d%d",&x,&y,&k);
		q[++cnt]=(NODE){x,y,k,i,2};
	}
	solve(1,cnt,-oo,oo);
	for (int i=1;i<=m;i++)
	printf("%d\n",ans[i]);
	return 0;
}

【模板】可持久化数组

#include<bits/stdc++.h>
using namespace std;
const int N = 1000005;
int iRt[N * 20], iSum[N * 20], iLs[N * 20], iRs[N * 20], iCnt, n, m, iA[N];

void build(int &cnt, int l, int r)
{
	cnt = ++iCnt;
	if(l == r) 
	{
		iSum[cnt] = iA[l];
		return;
	}
	int mid = (l + r) >> 1;
	build(iLs[cnt], l, mid);
	build(iRs[cnt], mid+1, r);
}

void ins(int &cnt, int pre, int l, int r, int q, int v)
{
	cnt = ++iCnt;
	iLs[cnt] = iLs[pre],iRs[cnt] = iRs[pre],iSum[cnt] = iSum[pre];
	if(l == r) 
	{
		iSum[cnt] = v;
		return ;
	}
	int mid = l + r >> 1;
	if(q <= mid) ins(iLs[cnt], iLs[pre], l, mid, q, v);
	if(q > mid) ins(iRs[cnt], iRs[pre], mid+1, r, q, v);
}

int query(int cnt, int l, int r, int q)
{
	if(l == r) return iSum[cnt];
	int mid = l + r >> 1;
	if(q <= mid) return query(iLs[cnt], l, mid, q);
	if(q > mid) return query(iRs[cnt], mid+1, r, q);
}

int main()
{
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++) scanf("%d", &iA[i]);
	build(iRt[0], 1, n);
	for(int i = 1, cnt, opt, x, y; i <= m; i++)
	{
		scanf("%d%d", &cnt, &opt);
		if(opt == 1)
		{
			scanf("%d%d", &x, &y);
			ins(iRt[i], iRt[cnt], 1, n, x, y);
		}
		else if(opt == 2)
		{
			scanf("%d", &x);
			printf("%d\n", query(iRt[cnt], 1, n, x));
			iRt[i] = iRt[cnt];
		}
	}
}

【模板】可持久化平衡树

#include<bits/stdc++.h>
using namespace std;
struct node
{
    int l,r;int size,rnd,v;
}t[500005*50];
int cnt,rt[500005];
void update(int k)
{
    t[k].size=t[t[k].l].size+t[t[k].r].size+1;
}
void newnode(int &k,int x)
{
    t[k=++cnt].v=x;t[k].size=1;t[k].rnd=rand();
}
int merge(int a,int b)
{
    if(!a||!b)return a+b;
    if(t[a].rnd>t[b].rnd)
    {
        int p=++cnt;t[p]=t[a];
        t[p].r=merge(t[p].r,b);
        update(p);return p;
    }
    else
    {
        int p=++cnt;t[p]=t[b];
        t[p].l=merge(a,t[p].l);
        update(p);return p;
    }
}
void split(int now,int k,int &x,int &y)
{
    if(!now)x=y=0;
    else
    {
        if(t[now].v<=k)
        {
            x=++cnt;t[x]=t[now];
            split(t[x].r,k,t[x].r,y);
            update(x);
        }
        else 
        {
            y=++cnt;t[y]=t[now];
            split(t[y].l,k,x,t[y].l);
            update(y);
        }
    }
}
void Delete(int &root,int w)
{
    int x=0,y=0,z=0;
    split(root,w,x,z);
    split(x,w-1,x,y);
    y=merge(t[y].l,t[y].r);
    root=merge(merge(x,y),z);
}
void Insert(int &root,int w)
{
    int x=0,y=0,z=0;
    split(root,w,x,y);
    newnode(z,w);
    root=merge(merge(x,z),y);
}
int getval(int k,int w)
{
    if(w==t[t[k].l].size+1)return t[k].v;
    else if(w<=t[t[k].l].size)return getval(t[k].l,w);
    else return getval(t[k].r,w-t[t[k].l].size-1);
}
int getkth(int &root,int w)
{
    int x,y;
    split(root,w-1,x,y);
    int ans=t[x].size+1;
    root=merge(x,y);
    return ans;
}
int getpre(int &root,int w)
{
    int x,y,k,ans;
    split(root,w-1,x,y);
    if(!x)return -2147483647;
    k=t[x].size;
    ans=getval(x,k);
    root=merge(x,y);
    return ans;
}
int getnex(int &root,int w)
{
    int x,y,ans;
    split(root,w,x,y);
    if(!y)return 2147483647;
    else ans=getval(y,1);
    root=merge(x,y);
    return ans;
}
int main()
{
    int n,f,w,tim;
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    {
        scanf("%d%d%d",&tim,&f,&w);
        rt[i]=rt[tim];
        if(f==1)Insert(rt[i],w);
        else if(f==2)Delete(rt[i],w);
        else if(f==3)printf("%d\n",getkth(rt[i],w));
        else if(f==4)printf("%d\n",getval(rt[i],w));
        else if(f==5)printf("%d\n",getpre(rt[i],w));
        else printf("%d\n",getnex(rt[i],w));
    }
    return 0;
}

【模板】可持久化文艺平衡树

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200005;
int ch[N<<6][3],val[N<<6],dat[N<<6],sz[N<<6],tag[N<<6],root[N],tot,x,y,z;
int can[N],cantop;
ll sum[N<<6];
void read(int &n)
{
	int fh=1,ans;
	char c;
	while (!isdigit(c=getchar())) if (c=='-') fh=-1;
	ans=c-48;
	while (isdigit(c=getchar())) ans=ans*10+c-48;
	n=ans*fh;
}
int new_node(int x)
{
	int a=cantop?can[cantop--]:++tot;
	sz[a]=1,sum[a]=val[a]=x,dat[a]=rand();
	return a;
}
int clone(int x)
{
	int a=cantop?can[cantop--]:++tot;
	sz[a]=sz[x],sum[a]=sum[x],val[a]=val[x],ch[a][0]=ch[x][0],ch[a][1]=ch[x][1],dat[a]=dat[x],tag[a]=tag[x];
	return a;
}
void down(int x)
{
	swap(ch[x][0],ch[x][1]);
	if (ch[x][0]) ch[x][0]=clone(ch[x][0]),tag[ch[x][0]]^=1;
	if (ch[x][1]) ch[x][1]=clone(ch[x][1]),tag[ch[x][1]]^=1;
	tag[x]=0;
}
void update(int x)
{
	sz[x]=1+sz[ch[x][0]]+sz[ch[x][1]];
	sum[x]=val[x]+sum[ch[x][0]]+sum[ch[x][1]];
}
void split(int now,int k,int &x,int &y)
{
	if (!now) {x=y=0;return;}
	if (tag[now]) down(now);
	if (sz[ch[now][0]]<k)
	{
		x=clone(now);
		split(ch[x][1],k-sz[ch[x][0]]-1,ch[x][1],y);
		update(x);
	}
	else 
	{
		y=clone(now);
		split(ch[y][0],k,x,ch[y][0]);
		update(y);
	}
}
int merge(int x,int y)
{
	if (!x||!y) return x+y;
	else if (dat[x]<dat[y])
	{
		if (tag[x]) down(x);
		ch[x][1]=merge(ch[x][1],y);
		update(x);
		return x;
	}
	else
	{
		if (tag[y]) down(y);
		ch[y][0]=merge(x,ch[y][0]);
		update(y);
		return y;
	}
}
void insert(int &root,int k,int a)
{
	split(root,k,x,y);
	root=merge(merge(x,new_node(a)),y);
}
void del(int &root,int a)
{
	split(root,a,x,z);
	split(x,a-1,x,y);
	can[++cantop]=y;
	root=merge(x,z);
}
void rev(int &root,int l,int r)
{
	split(root,l-1,x,y);
	split(y,r-l+1,y,z);
	tag[y]^=1;
	root=merge(x,merge(y,z));
}
ll query(int &root,int l,int r)
{
	split(root,l-1,x,y);
	split(y,r-l+1,y,z);
	ll ans=sum[y];
	root=merge(x,merge(y,z));
	return ans;
}
int main()
{
	int n;
	ll lastans=0;
	read(n);
	for (int i=1;i<=n;i++)
	{
		int v,opt,p,x,l,r;
		read(v),read(opt);
		root[i]=root[v];
		if (opt==1)
		{
			read(p),read(x);
			p^=lastans,x^=lastans;
			insert(root[i],p,x);
		}
		else if (opt==2)
		{
			read(p);
			p^=lastans;
			del(root[i],p);
		}
		else if (opt==3)
		{
			read(l),read(r);
			l^=lastans,r^=lastans;
			if (l>r) swap(l,r);
			rev(root[i],l,r);
		}
		else
		{
			read(l),read(r);
			l^=lastans,r^=lastans;
			if (l>r) swap(l,r);
			lastans=query(root[i],l,r);
			printf("%lld\n",lastans);
		}
	}
	return 0;
}
posted @ 2022-03-06 20:31  PassName  阅读(44)  评论(0编辑  收藏  举报