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

时隔一年,终于解决了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前插入数据value
inline 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 ;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值