之前在 codeforces 做过一道题目,算是第一次接触这东西吧。
题目的要求是,对一个n行n列的格子进行维护。单点更新或者区间更新。还有一个是返回第 k 次更新的状态。
直接就懵逼了。0.0 这是个什么鬼。
如果每次操作我都要记录状态的话,不用想,肯定内存会爆炸的。那我如果遇到该回去的操作时再原路返回,肯定时间会爆炸的。好吧,我知道我肯定要学一种新的数据结构了。
不过对这个题目后来我想了想,如果我不是在线做,而是离线来处理所有的查询呢?我们预先记录所有的返回操作。
这样是否可行呢?
下面主要来说一下在线的做法。
据说这个在线的做法有个很奇怪的名字:主席树。
伤心的是在BZOJ上找不到有关主席树的题目。因为只有 VIP 才可以看到。
这段内容其实在线段树的基础上很好理解。
我们一开始维护一个线段树。每次操作之后产生一个新树,不过这个树是在原来的基础上延伸出来的。
也就是对所操作的区间到根节点建立一个新的树,这个树和原来的树是连接在一起的。那么对于时间戳我们如何记录?每个延伸出来的节点,都是一个时间戳。一开始的树根节点时间为 1 ,下一次操作从原来的树延伸出所操作的区间,延伸出来的所有节点的根节点时间为 2,然后就没有了。0.0
初步理解是这个样子,日后若是有很好的表达,再来更新。0.0
贴下HDU 4348 的代码。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=100000+7;
int root[N],tot;
int Lson[N*30],Rson[N*30],add[N*30];
ll sum[N*30];
int n,m;
inline int bulid(int L,int R)
{
int rt=tot++;
add[rt]=0;
if (L==R)
{
scanf("%lld",&sum[rt]);
return rt;
}
int mid=(L+R)>>1;
Lson[rt]=bulid(L,mid);
Rson[rt]=bulid(mid+1,R);
sum[rt]=sum[Lson[rt]]+sum[Rson[rt]];
return rt;
}
inline int update(int rt,int L,int R,int x,int LL,int RR)
{
int k=tot++;
Lson[k]=Lson[rt];
Rson[k]=Rson[rt];
add[k]=add[rt];
sum[k]=sum[rt];
sum[k]+=(ll)x*(R-L+1);
if (LL==L && RR==R)
{
add[k]+=x;
return k;
}
int mid=(LL+RR)>>1;
if (R<=mid) Lson[k]=update(Lson[k],L,R,x,LL,mid);
else if (L>mid) Rson[k]=update(Rson[k],L,R,x,mid+1,RR);
else
{
Lson[k]=update(Lson[k],L,mid,x,LL,mid);
Rson[k]=update(Rson[k],mid+1,R,x,mid+1,RR);
}
return k;
}
inline ll query(int rt,int L,int R,int LL,int RR)
{
if (L==LL && R==RR) return sum[rt];
int mid=(LL+RR)>>1;
ll ret=(ll)add[rt]*(R-L+1);
if (R<=mid) return ret+query(Lson[rt],L,R,LL,mid);
else if (L>mid) return ret+query(Rson[rt],L,R,mid+1,RR);
else return ret+query(Lson[rt],L,mid,LL,mid)+query(Rson[rt],mid+1,R,mid+1,RR);
}
int main()
{
int x,L,R;
int now;
char ch[3];
bool f=false;
while (~scanf("%d%d",&n,&m))
{
if (f) puts("");
else f=true;
tot=0;
root[0]=bulid(1,n);
now=0;
while (m--)
{
scanf("%s",ch);
if (ch[0]=='C')
{
scanf("%d%d%d",&L,&R,&x);
now++;
root[now]=update(root[now-1],L,R,x,1,n);
}
else if (ch[0]=='Q')
{
scanf("%d%d",&L,&R);
printf("%lld\n",query(root[now],L,R,1,n));
}
else if (ch[0]=='H')
{
scanf("%d%d%d",&L,&R,&x);
printf("%lld\n",query(root[x],L,R,1,n));
}
else if (ch[0]=='B')
{
scanf("%d",&now);
}
}
}
return 0;
}