论在LCT上下放标记

来源:互联网 发布:考研大数据 编辑:程序博客网 时间:2024/06/06 09:53

对LCT上标记下放的一些研究

  • 实验题目
    Codevs1082线段树练习3

    http://codevs.cn/problem/1082/

  • 实验目的
    探究在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;}
0 0
原创粉丝点击