poj3468 splay(成段跟新 区间求和)

 用splay做了一遍。


      建树时是按照数列序号从小到大排好的,每个节点左子树的序号小于右子树的序号及这个节点本身。
由于查询[l,r]要伸展l-1,r+1所以我们要多加2个结点,保证边界处理时不出问题。由于这样每次查找l-1时,
要找的应该是l(r+1也是找r+2)。

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define ll __int64
#define key_value ch[ch[root][1]][0]
using namespace std;
const int MAXN=100010;
int pre[MAXN],ch[MAXN][2],key[MAXN],tot1,root;
int siz[MAXN],tot2,a[MAXN],n,s[MAXN];
ll lazy[MAXN],sum[MAXN];

void Treavel(int x)
{
    if(x)
    {
        Treavel(ch[x][0]);
        printf("结点%2d:左儿子 %2d 右儿子 %2d 父结点 %2d size=%2d,key=%2d add=%2d sum=%I64d\n",x,ch[x][0],ch[x][1],pre[x],siz[x],key[x],lazy[x],sum[x]);
        Treavel(ch[x][1]);
    }
}
void debug()
{
    printf("root:%d\n",root);
    Treavel(root);
}


void Newnode(int &rt,int father,int k)
{
    if(tot2)
        rt = s[--tot2];
    else
        rt = ++tot1;
    pre[rt] = father;
    key[rt] = k;
    siz[rt] = 1;
    lazy[rt] = 0;
    ch[rt][0] = ch[rt][1] = 0;
}
void pushup(int rt)
{
    siz[rt] = siz[ch[rt][0]] + siz[ch[rt][1]] + 1;
    sum[rt] = sum[ch[rt][0]] + sum[ch[rt][1]] + key[rt];
}
void pushdown(int rt)
{
    if(lazy[rt]){
        lazy[ch[rt][0]] += lazy[rt];
        lazy[ch[rt][1]] += lazy[rt];
        key[ch[rt][0]] += lazy[rt];
        key[ch[rt][1]] += lazy[rt];
        sum[ch[rt][0]] += (ll)siz[ch[rt][0]]*lazy[rt];
        sum[ch[rt][1]] += (ll)siz[ch[rt][1]]*lazy[rt];
        lazy[rt] = 0;
    }
}
void build(int &rt,int l,int r,int father)
{
    if(l > r)
        return ;
    int m = (l+r)/2;
    Newnode(rt,father,a[m]);
    build(ch[rt][0],l,m-1,rt);
    build(ch[rt][1],m+1,r,rt);
    pushup(rt);
}
void Init()
{
    int i,j;
    for(i=1; i<=n; i++){
        scanf("%d",&a[i]);
    }
    root = tot1 = tot2 = 0;
    ch[root][0] = ch[root][1] = key[root] = siz[root] = lazy[root] = pre[root] = sum[root] = 0;
    Newnode(root,0,-1);
    Newnode(ch[root][1],root,-1);//头尾各加入一个点
    build(key_value,1,n,ch[root][1]);//让所有数据夹在这两个点之间 由于树的结构 所以在ch[ch[root][1]][0]
    pushup(ch[root][1]);
    pushup(root);
}
void Rotate(int rt,int kind)
{
    int y = pre[rt];
    pushdown(y);
    pushdown(rt);
    ch[y][!kind] = ch[rt][kind];
    pre[ch[rt][kind]] = y;
    if(pre[y]){
        ch[pre[y]][ch[pre[y]][1]==y] = rt;
    }
    pre[rt] = pre[y];
    ch[rt][kind] = y;
    pre[y] = rt;
    pushup(y);
}
void splay(int rt,int goal)
{
    pushdown(rt);
    while(pre[rt] != goal)
    {
        if(pre[pre[rt]] == goal){
            Rotate(rt,ch[pre[rt]][0]==rt);
        }
        else {
            int y = pre[rt];
            int kind = ch[pre[y]][0]==y;
            if(ch[y][kind] == rt){
                Rotate(rt,!kind);
                Rotate(rt,kind);
            }
            else {
                Rotate(y,kind);
                Rotate(rt,kind);
            }
        }
    }
    pushup(rt);
    if(goal == 0)
        root = rt;
    
}
int Get_kth(int rt,int k)
{
    pushdown(rt);
    int t = siz[ch[rt][0]] + 1;
    if(t == k){
        return rt;
    }
    else if(t > k){
        return Get_kth(ch[rt][0],k);
    }
    else
        return Get_kth(ch[rt][1],k-t);
}
void updata(int l,int r,int v)
{
    splay(Get_kth(root,l),0);
    splay(Get_kth(root,r+2),root);
    key[key_value] += v;
    lazy[key_value] += v;
    sum[key_value] += (ll)v*siz[key_value];
}
ll query(int l,int r)
{
    splay(Get_kth(root,l),0);//由于开始的时候多添加了2个结点,所以编号都是在这2个结点之间的 所以查询的时候都要大1
    splay(Get_kth(root,r+2),root);
    return sum[key_value];
}
int main()
{
    int i,j,q;
    while(~scanf("%d%d",&n,&q))
    {
        Init();
        //debug();
        char s[10];
        while(q--)
        {
            scanf("%s",s);
            if(s[0] == 'C'){
                int x,y,z;
                scanf("%d%d%d",&x,&y,&z);
                updata(x,y,z);
            }
            else {
                int x,y;
                scanf("%d%d",&x,&y);
                printf("%I64d\n",query(x,y));
            }
        }
    }
}

 

你可能感兴趣的:(poj3468 splay(成段跟新 区间求和))