POJ 3468 A Simple Problem with Integers (Splay 区间更新、区间求和)

题目链接

POJ3468

题目大意

线段树区间更新的模板题,用Splay写一遍作为用Splay处理区间问题的模板题。

分析

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <string>
#define ls (rt<<1)
#define rs (rt<<1|1)
#define bit(x) (1<<(x))
using namespace std;
typedef long long LL;
const double pi=4*atan(1.0);
const int INF=0x3f3f3f3f;
const double eps=1e-6;
const int MAXN=100010;
const int MAXM=2*MAXN;
int a[MAXN],n;
struct SplayTree
{
    #define Key_value (nt[nt[root][1]][0])
    int root,tot;
    int nt[MAXN][2],pre[MAXN],val[MAXN],Size[MAXN],add[MAXN];
    LL sum[MAXN];
    void Newnode(int &rt,int fa,int v)
    {
        rt=++tot;
        nt[rt][0]=nt[rt][1]=0;
        pre[rt]=fa;
        val[rt]=v;
        add[rt]=sum[rt]=0;
        Size[rt]=1;
    }
    void Update_Add(int rt,int v)
    {
        if (rt==0) return;
        val[rt]+=v;
        add[rt]+=v;
        sum[rt]+=(LL) v*Size[rt];
    }
    void PushUp(int rt)
    {
        Size[rt]=1+Size[nt[rt][0]]+Size[nt[rt][1]];
        sum[rt]=sum[nt[rt][0]]+sum[nt[rt][1]]+val[rt];
    }
    void PushDown(int rt)
    {
        if (add[rt])
        {
            Update_Add(nt[rt][0],add[rt]);
            Update_Add(nt[rt][1],add[rt]);
            add[rt]=0;
        }
    }
    void Build(int &rt,int l,int r,int fa)///建立一棵完全平衡的二叉树,中序遍历即为原序列
    {
        if (l>r) return;
        int mid=(l+r)>>1;
        Newnode(rt,fa,a[mid]);
        Build(nt[rt][0],l,mid-1,rt);
        Build(nt[rt][1],mid+1,r,rt);
        PushUp(rt);
    }
    void Init()
    {
        for (int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        root=tot=0;
        nt[root][0]=nt[root][1]=pre[root]=Size[root]=val[root]=sum[root]=add[root]=0;
        Newnode(root,0,-1);
        Newnode(nt[root][1],root,-1);///为了能够正常提取区间,需要在序列的首位各添加一个-1
        Build(Key_value,1,n,nt[root][1]);
        PushUp(nt[root][1]);
        PushUp(root);
    }
    void Rotate(int x,int kind)///旋转:kind 0为左旋,1为右旋
    {
        int y=pre[x];
        PushDown(y);
        PushDown(x);
        nt[y][!kind]=nt[x][kind];
        pre[nt[x][kind]]=y;
        if(pre[y]) nt[pre[y]][nt[pre[y]][1]==y]=x;
        pre[x]=pre[y];
        nt[x][kind]=y;
        pre[y]=x;
        PushUp(y);
    }
    void Splay(int x,int goal)///Splay调整:将结点x调整到goal下面
    {
        PushDown(x);
        while(pre[x]!=goal)
        {
            if(pre[pre[x]]==goal)             ///单旋情况
                Rotate(x,nt[pre[x]][0]==x);
            else
            {
                int y=pre[x];
                int kind= nt[pre[y]][0]==y;
                if(nt[y][kind]==x)           ///之字形旋转
                {
                    Rotate(x,!kind);
                    Rotate(x,kind);
                }
                else                          ///一字形旋转
                {
                    Rotate(y,kind);
                    Rotate(x,kind);
                }
            }
        }
        PushUp(x);
        if(goal==0) root=x;
    }
    int Get_Kth(int rt,int k)///寻找序列中第k个数
    {
        PushDown(rt);
        int t=Size[nt[rt][0]]+1;
        if (k==t) return rt;///如果rt就是第k个数
        else if (k<t) return Get_Kth(nt[rt][0],k);
        else return Get_Kth(nt[rt][1],k-t);
    }
    void Add(int l,int r,int v)
    {
        Splay(Get_Kth(root,l),0);
        Splay(Get_Kth(root,r+2),root);
        Update_Add(Key_value,v);
        PushUp(nt[root][1]);
        PushUp(root);
    }
    LL Query(int l,int r)
    {
        Splay(Get_Kth(root,l),0);
        Splay(Get_Kth(root,r+2),root);
        return sum[Key_value];
    }
}st;
int main()
{
    char op[10];
    int Q,x,y,z;
    while (scanf("%d%d",&n,&Q)!=EOF)
    {
        st.Init();
        while (Q--)
        {
            scanf("%s",op);
            if (op[0]=='C')
            {
                scanf("%d%d%d",&x,&y,&z);
                st.Add(x,y,z);
            }
            if (op[0]=='Q')
            {
                scanf("%d%d",&x,&y);
                printf("%lld\n",st.Query(x,y));
            }

        }
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值