poj3468 A Simple Problem with Integers(spaly&&线段树)

第一道Splay   看了一天才能敲  当然模板还是仿照HH的。害羞

把代码理解通透也对splay的理解加深。

似乎没有线段树那么具体,因为不断的旋转操作会使splay 的形状发生变化。

但是二叉排序树原则没有变。然后在每一次query 和 update 的时候都需要的Rotate的两个操作就会得到想要的区间。

与线段树不相同的是他不用开n*4的空间  因为每一个结点代表自身和子树上的信息,在旋转之前就要pushdown,将结点定位以后还要pushup

在刘汝佳的大白上面说的也很清楚。

下面代码上的注释可以帮助理解splay


#include <cstdio>
#include <iostream>
#define inf 0x3f3f3f3f
#define maxn 222222
#define keyTree (ch[ch[root][1]][0])
//当把l-1放在根节点  r+1放在根节点的右子树
//那么根节点的右子树的左子树就是[l,r] 这个区间的所有值
using namespace std;
typedef long long LL;
int S[maxn],que[maxn],ch[maxn][2],pre[maxn],siz[maxn];
int root,top1,top2;
/*以上变量为Splay固有的*/
int add[maxn],val[maxn],a[maxn];
LL sum[maxn];
//debug来自HH
void Treaval(int x)
{
    if(x)
    {
        Treaval(ch[x][0]);
        printf("结点%2d:左儿子 %2d 右儿子 %2d 父结点 %2d size = %2d ,val = %2d , sum = %2lld \n",x,ch[x][0],ch[x][1],pre[x],siz[x],val[x],sum[x]);
        Treaval(ch[x][1]);
    }
}
void debug()
{
    printf("root=%d\n",root);
    Treaval(root);
}
//建一个新的节点,放在x,S是一个回收内存的内存池
//PRE是x的父亲节点
//v是把这个节点赋值为v
void New(int &x,int PRE,int v)
{
    if(top2)x=S[--top2];
    else x=++top1;

    ch[x][0]=ch[x][1]=0;
    siz[x]=1;
    pre[x]=PRE;
    /*special*/
    add[x]=0;
    val[x]=v;
    sum[x]=v;
}
void pushup(int x)/*special*/
{
    siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
    sum[x]=val[x]+sum[ch[x][0]]+sum[ch[x][1]];
}
void pushdown(int x)/*special*/
{
    if(add[x])
    {
        val[x]+=add[x];
        add[ch[x][0]]+=add[x];
        add[ch[x][1]]+=add[x];
        sum[ch[x][0]]+=(LL)siz[ch[x][0]]*add[x];
        sum[ch[x][1]]+=(LL)siz[ch[x][1]]*add[x];
        add[x]=0;
    }
}
//以上两个函数跟线段树大致相同

//建树,把中间节点作为根
void build(int &x,int s,int e,int f)
{
    if(s>e)return;
    int mid=(s+e)>>1;

    New(x,f,a[mid]);

    if(s<mid)build(ch[x][0],s,mid-1,x);
    if(e>mid)build(ch[x][1],mid+1,e,x);
    pushup(x);
}
//旋转,0为左旋  1为右旋
void Rotate(int x,int kind)
{
    int y=pre[x];
    pushdown(x);
    pushdown(y);
    ch[y][!kind]=ch[x][kind];
    pre[ch[x][kind]]=y;
    if(pre[y])ch[pre[y]][ch[pre[y]][1]==y]=x;
    pre[x]=pre[y];
    ch[x][kind]=y;
    pre[y]=x;
    pushup(y);
    //pushu(x);这里可以不用pushupx 是因为splay的时候会pushx
    //然而splay的时候也只动了x所以在后面pushx是一样的
    //而且复杂度会有很大影响
}
//把x节点放在goal的子树
//当然如果这个结点上的键值比goal大就是在goal的右子树,反之左子树
void Splay(int x,int goal)
{
    pushdown(x);
    while(pre[x]!=goal)
    {
        if(pre[pre[x]]==goal)
        Rotate(x,ch[pre[x]][0]==x);
        else
        {
            int y=pre[x];
            int kind=ch[pre[y]][0]==y;
            if(ch[y][kind]==x){
                Rotate(x,!kind);
                Rotate(x,kind);
            }
            else {
                Rotate(y,kind);
                Rotate(x,kind);
            }
        }
    }
    pushup(x);
    if(goal==0)root=x;
}
//把左数第k号节点放在goal下面
//调用了splay
void RorateTo(int k,int goal)
{
    int r=root;
    pushdown(r);
    while(siz[ch[r][0]]!=k)
    {
        if(k<siz[ch[r][0]])
        {
            r=ch[r][0];
        }
        else
        {
            k-=siz[ch[r][0]]+1;
            r=ch[r][1];
        }
        pushdown(r);
    }
    Splay(r,goal);
}
//删除节点x  并回收内存
void erase(int x)
{
    int y=pre[x];
    int head=0,tail=0;
    for(que[tail++]=x;head<tail;head++)
    {
        S[top2++]=que[head];
        if(ch[que[head]][0])que[tail++]=ch[que[head]][0];
        if(ch[que[head]][1])que[tail++]=ch[que[head]][1];
    }
    ch[y][ch[y][1]==x]=0;
    pushup(y);
}
//插入节点  键值为k
void insert(int k)
{
    int r=root;
    while(ch[r][val[r]<k])
    r=ch[r][val[r]<k];
    New(ch[r][val[r]<k],r,k);
    Splay(ch[r][val[r]<k],0);
}
//查询
LL query(int l,int r)
{
    RorateTo(l-1,0);
    RorateTo(r+1,root);
    return sum[keyTree];
}
//修改值  更新
void update(int l,int r)
{
    int v;
    scanf("%d",&v);
    RorateTo(l-1,0);
    RorateTo(r+1,root);
    add[keyTree]+=v;
    sum[keyTree]+=(LL)siz[keyTree]*v;
}
//初始化,包括建树 和 树上信息的输入
void init(int n)/*special*/
{
    root=top1=top2=0;
    ch[0][0]=ch[0][1]=siz[0]=pre[0]=0;
    add[0]=sum[0]=0;

    New(root,0,-1);
    New(ch[root][1],root,-1);

    siz[root]=2;

    for(int i=0;i<n;i++)scanf("%d",&a[i]);
    build(keyTree,0,n-1,ch[root][1]);
    pushup(ch[root][1]);
    pushup(root);
}
//得到前驱
int getpre(int x)
{
    int tmp=ch[x][0];
    if(tmp==0)return inf;
    while(ch[tmp][1])tmp=ch[tmp][1];
    return val[tmp];
}
//后继
int getnext(int x)
{
    int tmp=ch[x][1];
    if(tmp==0)return inf;
    while(ch[tmp][0])tmp=ch[tmp][0];
    return val[tmp];
}
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init(n);
        char str[5];
        while(m--)
        {
            int l,r;
            scanf("%s",str);
            scanf("%d%d",&l,&r);
            if(str[0]=='Q')
                printf("%lld\n",query(l,r));
            else update(l,r);
        }
    }
    return 0;
}
/*
10 3
1 2 3 4 5 6 7 8 9 10
Q 1 10
*/

下面是线段树



#include <iostream>
#include <cstdio>
#include <algorithm>
#define MAXN 100005
using namespace std;

long long tree[MAXN<<2];
long long add[MAXN<<2];

void pushdown(int num,int mid)
{
    if(add[num])
    {
        add[num<<1] +=  add[num];
        add[num<<1|1] += add[num];
        tree[num<<1] += ((mid-(mid>>1)) * add[num]);
        tree[num<<1|1] += ((mid>>1) * add[num]);
        add[num] = 0;
    }
}

void build(int num,int l,int r)
{
    add[num]=0;
    if(l==r)
    {
        scanf("%lld",&tree[num]);
        return;
    }
    int mid=(l+r)>>1;
    build(num<<1,l,mid);
    build(num<<1|1,mid+1,r);
    tree[num] = tree[num<<1] + tree[num<<1|1];
}

void update(int num,int s,int e,int l,int r,int val)
{
    if(s>=l && e<=r)
    {
        add[num] += val;
        tree[num] += (long long)val * (e - s + 1);
        return;
    }
    pushdown(num,e-s+1);
    int mid = (s+e)>>1;
    if(l<=mid)update(num<<1,s,mid,l,r,val);
    if(r>mid)update(num<<1|1,mid+1,e,l,r,val);

    tree[num] = tree[num<<1] + tree[num<<1|1];
}

long long query(int num,int s,int e,int l,int r)
{
    if(s==l && e==r)
    {
        return tree[num];
    }
    pushdown(num,e-s+1);
    int mid=(s+e)>>1;
    if(r<=mid)
    return query(num<<1,s,mid,l,r);
    else
    {
        if(l>mid)return query(num<<1|1,mid+1,e,l,r);
        else return query(num<<1,s,mid,l,mid) + query(num<<1|1,mid+1,e,mid+1,r);
    }
}

int main()
{
    int n,op;
    char str[10];

    scanf("%d%d",&n,&op);

    build(1,1,n);


    for(int i=1;i<=op;i++)
    {
        //printf("op = %d  %d\n",op,n);

        int a,b,c;
        scanf("%s",str);
        if(str[0]=='Q')
        {
            scanf("%d%d",&a,&b);
            printf("%lld\n",query(1,1,n,a,b));
        }
        else if(str[0]=='C')
        {
            scanf("%d%d%d",&a,&b,&c);
            update(1,1,n,a,b,c);
        }
    }
    return 0;
}


/*
10 10
1 2 3 4 5 6 7 8 9 10
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值