soj 4018upit解题报告 splay经典应用

来源:互联网 发布:java搭建项目的流程 编辑:程序博客网 时间:2024/05/24 22:43

时隔一年,终于解决了upit。。。

先给个题目连接:soj:upit

题目大意:

维护一段序列,序列有4种操作:

1.将[A,B]的值统一赋值为X

2.将[A,B]的值每一个都添加X的若干倍,方法为第一个+X,之后的+2X,3X,.....,kX,如此递推

3.在第C个数之前插入数值X

4.求区间[A,B]的和。

具体参考如下:


序列初始长度为N,开始给出这N个数,之后又M个操作,其中N,M<=100,000 ,A,B均合法,X<=100(其实在64位范围就行了),结果在64位整型内。

分析:

这道题是个典型的序列维护的问题,且维护为区间块,有插入操作,需要惰性维护。

splay和块状链表都可以完成这种维护操作。

以下splay解:

①:插入,求和自不必说。。

②:将[A,B]整体赋值的操作,显然在维护的节点上加入两个域即可满足要求。这里记为clear(int),clFlag(bool)标记该子树是否被赋值,如果被赋值则为true

        将clear赋值为-INF的时候表示未赋值可以省去变量clFlag

③:整体加法操作:

考虑之前有[A,B]+X,其子树记为R

后来因为R的旋转等因素将[A,B]分割成[A,C] + [C+1,B],这种时候A到C还是每个元素从+X,+2X,...,+kX,而C+1到B的部分则变成了+(k+1)X,....,+(k+m)X了,

也就是+kX+X,....,+kX+mX

其中k为子树[A,C]的节点数,m为[C+1,B]的节点数。

故每个区间由2个域构成,一个是基础的X,一个是附加的kx,分别记为addition和st

④:操作的组合

考虑对区间值进行操作的赋值和整体加法共2种

其组合无非是4种:赋值&赋值,赋值&加法,加法&加法,加法&赋值。

在区间维护的时候(lazy操作),第二次赋值显然要覆盖第一次赋值,先加法后赋值之前的加法也被覆盖

第二次加法累加到第一次加法上即可,还有就是先赋值后加法。

故在维护的时候有四种情况:

1.该节点仅有赋值,则赋值操作。

2.该节点仅有加法,则加法操作。

3.该节点均有,则必然是先赋值后加法,如果是先加法后赋值,则在上次维护的时候加法被赋值覆盖掉,仅剩赋值操作了。

4.均无。


ps:注意lazy操作,update更新sum,size等域的时候,前提是子树的数据sum和size已经是正确的。

在维护函数help的时候,计算保证该节点的sum和size是正确的,由下往上update。

故凡是在向下搜索节点的时候都要help,在伸展操作的时候要update。

注意update的前提,旋转后要先维护在update。


附个代码:

#include <stdio.h>#include <ctype.h>const int maxn = 200010;const int INF = 0x7fffffff;int root , n , m , k ;typedef long long ll;int array[maxn] ;void help(int);struct node{    int l,r,p;    int size,clear;    ll add,st,sum,key;    void init()    {        l = p = r = add = st = sum = key = 0 ;        clear = -INF ;        size = 1 ;    }} tree[maxn];inline void update(int x){    if(!x) return ;    tree[x].size = tree[tree[x].l].size+tree[tree[x].r].size+1;    tree[x].sum = tree[tree[x].l].sum + tree[tree[x].r].sum + tree[x].key ;}inline int get(){    int t = 0 ;    bool flag = 0 ;    char c;    while(!isdigit(c = getchar())&&c!='-');    if(c=='-') flag = 1;    else t = c - 48;    while(isdigit(c = getchar()))        t = (t << 1) + (t << 3) + c - 48;    return flag?-t:t;}inline void rotate(int x,int type){    int y = tree[x].p;    if(type)    {        tree[y].l = tree[x].r ;        tree[x].r = y ;        tree[tree[y].l].p = y ;    }    else    {        tree[y].r = tree[x].l ;        tree[x].l = y ;        tree[tree[y].r].p = y ;    }    tree[x].p = tree[y].p ;    tree[y].p = x ;    if( tree[tree[x].p].l == y ) tree[tree[x].p].l = x ;    else tree[tree[x].p].r = x ;    /*上次就是这个,这次还是!!!*/    help(tree[y].l);    help(tree[y].r);    update(y);    update(x);}inline void splay(int x,int rr)    //将x旋转到rr的儿子的位置{    if( x == rr )    return ;    if( rr == 0 ) root = x ;    while(tree[x].p!=rr)    {        int y = tree[x].p ;        int z = tree[y].p ;        if( z == rr )    rotate(x,tree[y].l==x);        else        {            if( tree[z].l == y )            {                if(tree[y].l==x)                {                    rotate(y,1);                    rotate(x,1);                }                else                {                    rotate(x,0);                    rotate(x,1);                }            }            else            {                if(tree[y].r==x)                {                    rotate(y,0);                    rotate(x,0);                }                else                {                    rotate(x,1);                    rotate(x,0);                }            }        }    }}inline void help(int x){    if(!x)    return ;    if ( tree[x].clear != -INF )    {        if( tree[x].add == 0 )        {            tree[x].key = tree[x].clear ;            tree[x].sum = (ll)tree[x].size * tree[x].clear ;            tree[tree[x].r].clear = tree[tree[x].l].clear = tree[x].clear;            tree[tree[x].r].add = tree[tree[x].l].add = 0 ;            tree[tree[x].r].st = tree[tree[x].l].st = 0 ;        }        else        {            int xl = tree[x].l ;            int xr = tree[x].r ;            tree[x].key = (ll)tree[x].clear + (ll)( tree[xl].size + 1 )*tree[x].add + tree[x].st ;            tree[x].sum = (ll)(tree[x].clear+tree[x].st)*tree[x].size+(ll)tree[x].size*(tree[x].size+1)/2*tree[x].add;            tree[xl].clear = tree[xr].clear = tree[x].clear;            tree[xl].add = tree[xr].add = tree[x].add ;            tree[xl].st = tree[x].st ;            tree[xr].st = tree[x].st + (ll)(tree[xl].size+1)*tree[xl].add;        }    }    else    {        if( tree[x].add != 0 )        {            int xl = tree[x].l ;            int xr = tree[x].r ;            tree[x].key += (ll)(tree[xl].size+1)*tree[x].add+tree[x].st ;            tree[x].sum += (ll)tree[x].size * tree[x].st + (ll)tree[x].size * (tree[x].size+1) / 2 * tree[x].add ;            tree[xl].add += tree[x].add ;            tree[xl].st += tree[x].st ;            tree[xr].add += tree[x].add ;            tree[xr].st += (ll)(tree[xl].size+1)*tree[x].add + tree[x].st ;        }    }    tree[x].st = tree[x].add = 0 ;    tree[x].clear = -INF ;}void insert(int value,int pos);inline void build(int l,int r){    /*k = 3 ;    tree[1].init();    tree[2].init();    tree[1].r = 2 ;    tree[2].p = 1 ;    update(1);    root = 1 ;    int i ;    for( i = n ; i >= 1 ; i--) insert(array[i],1);*/    int temp = k ;    //tree[k].init();    if( l == r )    {        tree[k].sum = tree[k].key = array[l-1] ;        tree[k].l = tree[k].r = 0 ;        tree[k].clear = -INF;        tree[k].add = tree[k].st = 0 ;        tree[k].size = 1 ;    }    else    {        int mid = (l+r)>>1 , j = k ;        tree[k].key = array[mid-1] ;        if(mid==l)    tree[k].l = 0 ;        else        {            tree[j].l = ++k;            tree[k].p = j ;            build(l,mid-1);        }        tree[j].r = ++k;        tree[k].p = j ;        build(mid+1,r);        tree[j].clear = -INF;        tree[j].add = tree[j].st = 0 ;        update(j);    }}inline int Rank(int key)    //查找第k个元素{    int cur = root , j = 0 ;    while(1)    {        help(cur);        if( 1+tree[tree[cur].l].size+j == key )    return cur ;        else if( tree[tree[cur].l].size+j+1 < key )        {            j += (tree[tree[cur].l].size+1) ;            cur = tree[cur].r ;        }        else cur = tree[cur].l ;    }}inline ll querySum(int l,int r){    int temp = Rank(l);    int rr = Rank(r+2);    splay(temp,0);    splay(rr,temp);    help(temp);    help(rr);    help(tree[rr].l);    help(tree[tree[rr].l].l);    help(tree[tree[rr].l].r);    update(tree[rr].l);    return tree[tree[rr].l].sum ;}//在pos前插入数据valueinline void insert(int value,int pos){    int posTh = Rank(pos);    splay(posTh,0);    int temp = tree[posTh].r ;    help(temp);    while( tree[temp].l != 0 ) {    temp = tree[temp].l ; help(temp);   }    tree[temp].l = ++k;    int newnode = k ;    tree[newnode].key = value ;    tree[newnode].size = 1 ;    tree[newnode].sum = value ;    tree[newnode].l = tree[newnode].r = 0 ;    tree[newnode].p = temp ;    tree[newnode].clear = -INF;    tree[newnode].add = tree[newnode].st = 0 ;    splay(newnode,0);}void clear(int l,int r,int x){    int temp = Rank(l);    int tmp = Rank(r+2);    splay(temp,0);    splay(tmp,temp);    tree[tree[tmp].l].clear = x ;    tree[tree[tmp].l].add = 0 ;}void addInterval(int l,int r,int x){    int temp = Rank(l);    int tmp = Rank(r+2);    splay(temp,0);    splay(tmp,temp);    int interval = tree[tmp].l ;    tree[interval].add += x ;}inline void work(){    int l , r , x , y , op ;    while(m--)    {        op = get();        if( op == 1 )        {            l = get();            r = get();            x = get();            clear(l,r,x);        }        else if( op == 4 )        {            l = get();            r = get();            printf("%lld\n",querySum(l,r));        }        else if( op == 3 )        {            x = get();            y = get();            insert(y,x);        }        else        {            l = get();            r = get();            x = get();            addInterval(l,r,x);        }    }}inline void getArray(char str[],int value[],int st)    //将字符串str中的数字写到value中去,自st开始,返回最后的num,无前导零{    int i , j , t , negative ;    for( i = 0 , j = st ; str[i] ; i++) if((str[i]>=48&&str[i]<=57)||str[i]=='-')    {        if(str[i]=='-') negative = -1 , i++;        else negative = 1 ;        t = 0 ;        while(str[i]>=48&&str[i]<=57) t = (t<<1)+(t<<3)+(str[i++]^48) ;        value[j++] = t*negative ;    }}char str[maxn*5] ;int main(){    int i , j ;    while( scanf("%d%d",&n,&m) == 2 )    {        tree[0].init();        tree[0].size = 0 ;        tree[1].p = 0 ;        root = k = 1 ;getchar();        gets(str);        getArray(str,array,1);        build(1,n+2);        work();    }    return 0 ;}


原创粉丝点击