对LCT上标记下放的一些研究
实验题目
Codevs1082线段树练习3实验目的
探究在LCT上下放标记的方式与特点,并与线段树和平衡树Splay做一下比较。如何用LCT表示一个数列
我用了种“猥琐”的方法——各结点前连后建成一条链。
其实看起来最快的方法是先用O(n)的时间建成一棵平衡树,然后用类似树链剖分中轻重链剖分的办法,把这棵树剖成实链和虚链,每条链再建成平衡的Splay。如何用LCT提取一个区间
LCT以Splay操作为基础,所以其提取区间的方式和Splay类似,而与线段树不同。若要提取区间[x,y],则先把x置为整棵树的根,再对y进行access和Splay操作。这样以后,x和y在一条实链上,并且x是树根保证这条实链没有x之前的结点,access(y)操作保证在Splay中y没有右子结点。所以此时结点y代表了区间[x.y]的一切!此时y对应的所有域(比如对应实链的和)都是区间[x,y]。如何用LCT对一段区间打上标记
我们以区间加一个数为例。
利用上文的办法,我们可以用不多于3行代码提取出所求的区间。对区间打标记可以借鉴NOI2005维护数列一题在Splay上打标记的方法,即直接在y上标记,更改y,然后进行标记下传即可。更改y需要LCT维护一个size域,表示Splay树中子树的大小。当我们用上述方式提取出一段区间[x,y]时,y不多不少地代表了一切。区间[x,y]的结点个数就是size(y)的值。区间加上一个数时,y结点对应的权值加上这个数,y对应的总和加上size(y)和这个数的乘积,然后标记数组加上这个数。如何在LCT上下传标记
我们以区间加一个数为例。
首先,update操作维护size域和sum的值;
pushdown在下传完反转标记的时候(或之前),下传加标记,此时同时更改子结点的值和子结点的sum值,其中sum的值借助子结点的size域实现更新,然后把标记下传至子结点。LCT上打标记和下放标记的特点
因为提取区间[x,y]后,y代表了一切,所以区间修改在LCT中体现为对y结点的单点修改。区间下放标记时对子结点的修改和修改y的过程极其类似,所以可以设计成同一个函数。Code
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 200005, nil = 0;
bool rev[N];
int lc[N], rc[N], fa[N], sz[N];
int n, m, S[N];
ll val[N], sum[N], d[N];
inline void update(int);
inline void pushdown(int);
void zig(int);
void zag(int);
void splay(int);
inline int access(int);
inline void makeroot(int);
inline void lnk(int , int);
inline void cut(int , int);
inline ll query(int , int);
inline void add(int , ll);
int main()
{
scanf("%d", &n);
int x, y, opt;
ll z;
for(int i = 1; i <= n; ++i)
{
scanf("%lld", &z);
access(i); splay(i);
add(i, z);
}
for(int i = 1; i < n; ++i) lnk(i, i + 1);
scanf("%d", &m);
while(m--)
{
scanf("%d", &opt);
if(opt == 1)
{
scanf("%d%d%lld", &x, &y, &z);
makeroot(x);
access(y); splay(y);
add(y, z);
}
else
{
scanf("%d%d", &x, &y);
printf("%lld\n", query(x, y));
}
}
return 0;
}
inline void update(int rot)
{
sz[rot] = sz[lc[rot]] + sz[rc[rot]] + 1;
sum[rot] = val[rot] + sum[lc[rot]] + sum[rc[rot]];
}
inline void pushdown(int rot)
{
if(rev[rot])
{
swap(lc[rot], rc[rot]);
rev[lc[rot]] ^= 1;
rev[rc[rot]] ^= 1;
rev[rot] = false;
}
if(d[rot] != 0)
{
add(lc[rot], d[rot]); add(rc[rot], d[rot]);
d[rot] = 0;
}
}
void zig(int x)
{
int y = fa[x];
lc[y] = rc[x];
fa[rc[x]] = y;
rc[x] = y;
fa[x] = fa[y];
if(y == lc[fa[y]]) lc[fa[y]] = x;
else if(y == rc[fa[y]]) rc[fa[y]] = x;
fa[y] = x;
update(y);
}
void zag(int x)
{
int y = fa[x];
rc[y] = lc[x];
fa[lc[x]] = y;
lc[x] = y;
fa[x] = fa[y];
if(y == lc[fa[y]]) lc[fa[y]] = x;
else if(y == rc[fa[y]]) rc[fa[y]] = x;
fa[y] = x;
update(y);
}
void splay(int x)
{
int top = 0;
S[top++] = x;
for(int i = x; i == lc[fa[i]] || i == rc[fa[i]]; i = fa[i])
{
S[top++] = fa[i];
}
while(top--) pushdown(S[top]);
int y = nil, z = nil;
while(x == lc[fa[x]] || x == rc[fa[x]])
{
y = fa[x]; z = fa[y];
if(x == lc[y])
{
if(y == lc[z]) zig(y);
zig(x);
}
else
{
if(y == rc[z]) zag(y);
zag(x);
}
}
update(x);
}
inline int access(int x)
{
int y;
for(y = nil; x != nil; y = x, x = fa[x])
{
splay(x);
rc[x] = y;
update(x);
}
return y;
}
inline void makeroot(int x)
{
access(x); splay(x); rev[x] ^= 1;
}
inline void lnk(int x, int y)
{
makeroot(x);
fa[x] = y;
}
inline void cut(int x, int y)
{
makeroot(x);
access(y); splay(y);
fa[x] = lc[y] = nil;
update(y);
}
inline ll query(int x, int y)
{
makeroot(x);
access(y); splay(y);
return sum[y];
}
inline void add(int x, ll mrk)
{
val[x] += mrk;
sum[x] += sz[x] * mrk;
d[x] += mrk;
}