Splay_Tree 模板(区间修改,旋转操作)

1.旋转操作

#define MAXN 100100

bool Add[MAXN];//延迟标记

struct Splay_Tree
{
    int cnt, rt;//cnt为节点数,rt == root

    struct Tree{
        int key;//关键字
        int num, size;//num是这个节点有多少重复,size是以这个节点为根的子树大小。
        int fa, son[2];
    }T[MAXN];

    inline void init()
    {
        cnt = 0;//初始化超级根节点(标记为0的节点)
        T[0].size = 0;
        rt = 0;
        memset(Add,0,sizeof(Add));//开始初始化0
    }
    inline void PushUp(int x)
    {
        T[x].size=T[T[x].son[0]].size+T[T[x].son[1]].size+T[x].num;
    }

    inline void PushDown(int x)
    {
        //翻转操作,这一步最为关键
        if(Add[x])
        {
            swap(T[x].son[0],T[x].son[1]);
            Add[T[x].son[0]] ^= 1;
            Add[T[x].son[1]] ^= 1;
            Add[x] = 0;
        }
    }

    inline int Newnode(int key, int fa) //新建一个节点并返回
    {
        ++cnt;
        T[cnt].key=key;
        T[cnt].num=T[cnt].size=1;
        T[cnt].fa=fa;
        T[cnt].son[0]=T[cnt].son[1]=0;
        return cnt;
    }

    inline void Rotate(int x, int p) //0左旋 1右旋
    {
        int y=T[x].fa;
        PushDown(y);//你是说这个有问题。
        PushDown(x);
        T[y].son[!p]=T[x].son[p];
        T[T[x].son[p]].fa=y;
        T[x].fa=T[y].fa;
        if(T[x].fa)
            T[T[x].fa].son[T[T[x].fa].son[1] == y]=x;
        T[x].son[p]=y;
        T[y].fa=x;
        PushUp(y);
        PushUp(x);
    }

    void Splay(int x, int To) //将x节点移动到To的子节点中
    {
        while(T[x].fa != To)
        {

            if(T[T[x].fa].fa == To)
            {
                //在这里面得
                PushDown(T[x].fa);
                PushDown(x);
                Rotate(x, T[T[x].fa].son[0] == x);
            }
            else
            {
                int y=T[x].fa, z=T[y].fa;
                PushDown(z);
                PushDown(y);
                PushDown(x);
                int p=(T[z].son[0] == y);
                if(T[y].son[p] == x)
                    Rotate(x, !p), Rotate(x, p); //之字旋
                else
                    Rotate(y, p), Rotate(x, p); //一字旋
            }
        }
        if(To == 0) rt=x;
    }

    int GetPth(int p, int To) //返回第p小的节点 并移动到To的子节点中
    {
        if(!rt || p > T[rt].size) return 0;
        int x=rt;
        while(x)
        {
            PushDown(x);
            if(p >= T[T[x].son[0]].size+1 && p <= T[T[x].son[0]].size+T[x].num)
                break;
            if(p > T[T[x].son[0]].size+T[x].num)
            {
                p-=T[T[x].son[0]].size+T[x].num;
                x=T[x].son[1];
            }
            else
                x=T[x].son[0];
        }
        Splay(x, 0);
        return x;
    }

    int Find(int key) //返回值为key的节点 若无返回0 若有将其转移到根处
    {
        if(!rt) return 0;
        int x=rt;
        while(x)
        {
            PushDown(x);
            if(T[x].key == key) break;
            x=T[x].son[key > T[x].key];
        }
        if(x) Splay(x, 0);
        return x;
    }

    int Prev() //返回根节点的前驱 非重点
    {
        if(!rt || !T[rt].son[0]) return 0;
        int x=T[rt].son[0];
        PushDown(x);
        while(T[x].son[1])
        {
            x=T[x].son[1];
            PushDown(x);
        }
        Splay(x, 0);
        return x;
    }

    int next() //返回根结点的后继 非重点
    {
        if(!rt || !T[rt].son[1]) return 0;
        int x=T[rt].son[1];
        PushDown(x);
        while(T[x].son[0])
        {
            x=T[x].son[0];
            PushDown(x);
        }
        Splay(x, 0);
        return x;
    }

    void Insert(int key) //插入key值
    {
        if(!rt)
            rt=Newnode(key, 0);
        else
        {
            int x=rt, y=0;
            while(x)
            {
                PushDown(x);
                y=x;
                if(T[x].key == key)
                {
                    T[x].num++;
                    T[x].size++;
                    break;
                }
                T[x].size++;//既然一定调整
                x=T[x].son[key > T[x].key];
            }
            if(!x)
                x = T[y].son[key > T[y].key] = Newnode(key, y);
            Splay(x, 0);
        }
    }

    void Delete()//直接改成删除根节点
    {
        if(rt == 0) return ;//已经没有节点了
        if(T[rt].num > 1)
        {
            T[rt].num --;
            T[rt].size --;
            return ;
        }
        if(T[rt].son[0] == 0)
        {
            rt = T[rt].son[1];
            T[rt].fa = 0;
        }
        else
        {
            int preid = T[rt].son[0];
            PushDown(preid);
            while(T[preid].son[1])
            {
                preid = T[preid].son[1];
                PushDown(preid);
            }
            Splay(preid, rt);
            T[preid].son[1] = T[rt].son[1];
            T[ T[rt].son[1] ].fa = preid;
            rt = preid;
            T[rt].fa = 0;
            PushUp(rt);
        }
    }

//    int GetRank(int key) //获得值<=key的节点个数 并将其转移到根处 若<key只需将<=换为<
//    {
//        //我没有写PUSH_UP 和 PUSH_DOWN
//        if(!rt) return 0;
//        int x=rt, ret=0, y=0;
//        while(x)
//        {
//            y=x;
//            if(T[x].key <= key)
//            {
//                ret += T[T[x].son[0]].size + T[x].num;
//                x=T[x].son[1];
//            }
//            else
//                x=T[x].son[0];
//        }
//        Splay(y, 0);
//        return ret;
//    }

//    这个删除太丑了
//    void Delete(int l, int r) //删除值在[l, r]中的所有节点 l!=r
//    {
//        if(!Find(l)) Insert(l);// 你这样写真的好吗? 泥煤
//        int p=Prev();
//        if(!Find(r)) Insert(r);
//        int q=next();
//        if(!p && !q)
//        {
//            rt=0;
//            return;
//        }
//        if(!p)
//        {
//            T[rt].son[0]=0;
//            PushUp(rt);
//            return;
//        }
//        if(!q)
//        {
//            Splay(p, 0);
//            T[rt].son[1]=0;
//            PushUp(rt);
//            return;
//        }
//        Splay(p, q);
//        T[p].son[1]=0;
//        PushUp(p);
//        PushUp(q);
//    }

    void display(int x)
    {
        if(x==0) return ;
        PushDown(x);
        display(T[x].son[0]);
        printf("%d ",T[x].key);
        display(T[x].son[1]);
    }

}spt;

//
//int main() {
//    //SPT 独特的旋转操作!
//    int n;
//    while(scanf("%d",&n) && n)
//    {
//        spt.init();
//        for(int i=1;i<=n;i++)
//        {
//            int tmp;
//            scanf("%d",&tmp);
//            g[i].key = tmp;
//            g[i].id = i;
//            spt.Insert(i);
//        }
//
//        sort(g+1,g+1+n,cmp);
//        for(int i=1;i<=n;i++)//开始旋转
//        {
//            //step one 将当前最小的点,移动到树根处
//            spt.Splay(g[i].id, 0);
//
//            //step two 将整个左子树旋转
//            int sonid = spt.T[g[i].id].son[0];
//            printf("%d",spt.T[sonid].size+i);
//            if(i!=n) printf(" ");
//            if(sonid != 0)
//            {
//                Add[sonid] ^= 1;//修改区间
//            }
//            spt.Delete();
//
//            //每次操作之后,都把结果打印一遍
//            //spt.display(spt.rt);
//            //printf("\n");
//        }
//        printf("\n");
//    }
//    return 0;
//}

2.成段更#define MAXN 200100


long long Add[MAXN];//延迟标记

struct Splay_Tree
{
    int cnt, rt;//cnt为节点数,rt == root

    struct Tree{
        long long K;
        long long sumk;
        int key;//关键字
        int num, size;//num是这个节点有多少重复,size是以这个节点为根的子树大小。
        int fa, son[2];
    }T[MAXN];

    inline void init()
    {
        cnt = 0;//初始化超级根节点(标记为0的节点)
        T[0].size = 0;
        T[0].sumk = 0;
        T[0].K = 0;
        rt = 0;
        memset(Add,0,sizeof(Add));
    }
    inline void PushUp(int x)
    {
        T[x].size=T[T[x].son[0]].size+T[T[x].son[1]].size+T[x].num;
        T[x].sumk=T[T[x].son[0]].sumk+T[T[x].son[1]].sumk+T[x].K;
    }

    inline void PushDown(int x)
    {
        if(Add[x])
        {
            if(T[x].son[0])//
            {
                T[T[x].son[0]].K += Add[x];
                T[T[x].son[0]].sumk += T[T[x].son[0]].size*Add[x];
                Add[T[x].son[0]]+=Add[x];
            }
            if(T[x].son[1])
            {
                T[T[x].son[1]].K+=Add[x];
                T[T[x].son[1]].sumk += T[T[x].son[1]].size*Add[x];
                Add[T[x].son[1]]+=Add[x];
            }
            Add[x]=0;//不管子节点有没有,这层一定往下推,没有子节点相当于标记无效。
        }
    }

    inline int Newnode(int key, int fa,int K) //新建一个节点并返回
    {
        ++cnt;
        T[cnt].K = T[cnt].sumk = K;
        T[cnt].key=key;
        T[cnt].num=T[cnt].size=1;
        T[cnt].fa=fa;
        T[cnt].son[0]=T[cnt].son[1]=0;
        return cnt;
    }

    inline void Rotate(int x, int p) //0左旋 1右旋
    {
        int y=T[x].fa;
        PushDown(y);//这里是不是必须的,我感觉从小往上不需要往上推
        PushDown(x);
        T[y].son[!p]=T[x].son[p];
        T[T[x].son[p]].fa=y;
        T[x].fa=T[y].fa;
        if(T[x].fa)
            T[T[x].fa].son[T[T[x].fa].son[1] == y]=x;
        T[x].son[p]=y;
        T[y].fa=x;
        PushUp(y);
        PushUp(x);
    }

    void Splay(int x, int To) //将x节点移动到To的子节点中
    {
        while(T[x].fa != To)
        {
            if(T[T[x].fa].fa == To)
                Rotate(x, T[T[x].fa].son[0] == x);
            else
            {
                int y=T[x].fa, z=T[y].fa;
                int p=(T[z].son[0] == y);
                if(T[y].son[p] == x)
                    Rotate(x, !p), Rotate(x, p); //之字旋
                else
                    Rotate(y, p), Rotate(x, p); //一字旋
            }
        }
        if(To == 0) rt=x;
    }

    int GetPth(int p, int To) //返回第p小的节点 并移动到To的子节点中
    {
        if(!rt || p > T[rt].size) return 0;
        int x=rt;
        while(x)
        {
            PushDown(x);
            if(p >= T[T[x].son[0]].size+1 && p <= T[T[x].son[0]].size+T[x].num)
                break;
            if(p > T[T[x].son[0]].size+T[x].num)
            {
                p-=T[T[x].son[0]].size+T[x].num;
                x=T[x].son[1];
            }
            else
                x=T[x].son[0];
        }
        Splay(x, 0);
        return x;
    }

    int Find(int key) //返回值为key的节点 若无返回0 若有将其转移到根处
    {
        if(!rt) return 0;
        int x=rt;
        while(x)
        {
            PushDown(x);
            if(T[x].key == key) break;
            x=T[x].son[key > T[x].key];
        }
        if(x) Splay(x, 0);
        return x;
    }

    int Prev() //返回根节点的前驱 非重点
    {
        if(!rt || !T[rt].son[0]) return 0;
        int x=T[rt].son[0];
        while(T[x].son[1])
        {
            PushDown(x);
            x=T[x].son[1];
        }
        Splay(x, 0);
        return x;
    }

    int next() //返回根结点的后继 非重点
    {
        if(!rt || !T[rt].son[1]) return 0;
        int x=T[rt].son[1];
        while(T[x].son[0])
        {
            PushDown(x);
            x=T[x].son[0];
        }
        Splay(x, 0);
        return x;
    }

    void Insert(int key,int K) //插入key值
    {
        if(!rt)
            rt=Newnode(key, 0, K);
        else
        {
            int x=rt, y=0;
            while(x)
            {
                PushDown(x);
                y=x;
                if(T[x].key == key)
                {
                    T[x].num++;
                    T[x].size++;
                    break;
                }
                T[x].size++;//既然一定调整
                x=T[x].son[key > T[x].key];
            }
            if(!x)
                x = T[y].son[key > T[y].key] = Newnode(key, y, K);
            Splay(x, 0);
        }
    }
    

//    void Delete()//直接改成删除根节点


//    {


//        if(rt == 0) return ;//已经没有节点了


//        if(T[rt].num > 1)


//        {


//            T[rt].num --;


//            T[rt].size --;


//            return ;


//        }


//        if(T[rt].son[0] == 0)


//        {


//            rt = T[rt].son[1];


//            T[rt].fa = 0;


//        }


//        else


//        {


//            int preid = T[rt].son[0];


//            PushDown(preid);


//            while(T[preid].son[1])


//            {


//                preid = T[preid].son[1];


//                PushDown(preid);


//            }


//            Splay(preid, rt);


//            T[preid].son[1] = T[rt].son[1];


//            T[ T[rt].son[1] ].fa = preid;


//            rt = preid;


//            T[rt].fa = 0;


//            PushUp(rt);


//        }


//    }

void Delete(int key) //删除值为key的节点1个,这个操作可能有问题。。。
    {
        int x=Find(key);
        if(!x) return;
        if(T[x].num>1)
        {
            T[x].num--;
            PushUp(x);
            return;
        }
        int y=T[x].son[0];
        while(T[y].son[1])
            y=T[y].son[1];
        int z=T[x].son[1];
        while(T[z].son[0])
            z=T[z].son[0];
        if(!y && !z)
        {
            rt=0;
            return;
        }
        if(!y)
        {
            Splay(z, 0);
            T[z].son[0]=0;
            PushUp(z);
            return;
        }
        if(!z)
        {
            Splay(y, 0);
            T[y].son[1]=0;
            PushUp(y);
            return;
        }
        Splay(y, 0);
        Splay(z, y);
        T[z].son[0]=0;
        PushUp(z);
        PushUp(y);
    }

    int GetRank(int key) //获得值<=key的节点个数 并将其转移到根处 若<key只需将<=换为<
    {
        if(!rt) return 0;
        int x=rt, ret=0, y=0;
        while(x)
        {
            y=x;
            if(T[x].key <= key)
            {
                ret += T[T[x].son[0]].size + T[x].num;
                x=T[x].son[1];
            }
            else
                x=T[x].son[0];
        }
        Splay(y, 0);
        return ret;
    }

//    这个删除太丑了
//    void Delete(int l, int r) //删除值在[l, r]中的所有节点 l!=r
//    {
//        if(!Find(l)) Insert(l);// 你这样写真的好吗? 泥煤
//        int p=Prev();
//        if(!Find(r)) Insert(r);
//        int q=next();
//        if(!p && !q)
//        {
//            rt=0;
//            return;
//        }
//        if(!p)
//        {
//            T[rt].son[0]=0;
//            PushUp(rt);
//            return;
//        }
//        if(!q)
//        {
//            Splay(p, 0);
//            T[rt].son[1]=0;
//            PushUp(rt);
//            return;
//        }
//        Splay(p, q);
//        T[p].son[1]=0;
//        PushUp(p);
//        PushUp(q);
//    }
}spt;
时间: 2024-10-01 04:10:51

Splay_Tree 模板(区间修改,旋转操作)的相关文章

二维树状数组模板(区间修改+区间查询)

二维树状数组模板(区间修改+区间查询) 例题:JOIOI上帝造题的七分钟 一共两种操作: \(L\ x_1\ y_1\ x_2\ y_2\ d\):把\((x_1,y_1)\),\((x_2,y_2)\)这个矩形内所有元素加\(d\). \(k\ x_1\ y_1\ x_2\ y_2\):查询\((x_1,y_1)\),\((x_2,y_2)\)这个矩形内所有元素的和. 代码如下: #include<bits/stdc++.h> #define RG register #define IL i

[线段树模板] 区间修改 区间查询(详注)

输入 每个测试点(输入文件)有且仅有一组测试数据. 每组测试数据的第1行为一个整数N,意义如前文所述. 每组测试数据的第2行为N个整数,分别描述每种商品的重量,其中第i个整数表示标号为i的商品的重量Pi. 每组测试数据的第3行为一个整数Q,表示小Hi进行的操作数. 每组测试数据的第N+4~N+Q+3行,每行分别描述一次操作,每行的开头均为一个属于0或1的数字,分别表示该行描述一个询问和一次商品的价格的更改两种情况.对于第N+i+3行,如果该行描述一个询问,则接下来为两个整数Li, Ri,表示小H

P3368 【模板】树状数组 2 单点查询与区间修改

题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数数加上x 2.求出某一个数的和 输入输出格式 输入格式: 第一行包含两个整数N.M,分别表示该数列数字的个数和操作的总个数. 第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值. 接下来M行每行包含2或4个整数,表示一个操作,具体如下: 操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k 操作2: 格式:2 x 含义:输出第x个数的值 输出格式: 输出包含若干行整数,即为所有操作2的结

线段树区间修改模板

本来打算把大白书第三章一口气攻下来的,但是这个线段树也是卡了好久. 不敢过题太快,怕自己走马观花到头来结果什么都不会. 可也不能再拖了,在做题中也许有更多的体会. 模板一:1 L R v 表示区间[L, R]所有元素都加上v2 L R   表示查询区间[L, R]的sum, min, maxsumv[o]的定义为:如果只执行节点o及其子孙节点的中的add操作,节点o对应区间中所有数之和 1 //线段树区间修改 2 //1 L R v 表示区间[L, R]所有元素都加上v 3 //2 L R 表示

洛谷 P3368 【模板】树状数组 2 如题(区间修改+单点查询)

P3368 [模板]树状数组 2 时空限制1s / 128MB 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数数加上x 2.求出某一个数的和 输入输出格式 输入格式: 第一行包含两个整数N.M,分别表示该数列数字的个数和操作的总个数. 第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值. 接下来M行每行包含2或4个整数,表示一个操作,具体如下: 操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k 操作2: 格式:2 x 含义:输出

【模板】线段树区间修改

区间修改: 区间修改过程类似于区间询问,例如将[ul, ur]内的所有元素都加上v,则进行如下操作: 当当前区间被区间[ul, ur]所包含时, 当前的节点值加上区间长度(r - l  + 1)乘以v 对当前节点的lazy-tag加上v,修改结束 否则,将当前节点的lazy-tag下传,分别修改左孩子和右孩子(一定条件下),然后更新此节点的值 lazy-tag下传: 如果当前节点没有lazy-tag,直接return 否则:1. 将左孩子右孩子分别加上lazy-tag * 区间长度的值  2.

hdu1698 Just a Hook 【区间修改】(模板题)

题目链接:https://vjudge.net/contest/182746#problem/E 题目大意: 一段线段由n条小线段组成,每次操作把一个区间的小线段变成金银铜之一(金的价值为3,银为2,铜为1),最初可当做全为铜:最后求这条线段的总价值. 解题分析: 此题为线段树区间修改的一道模板题,区间修改的重点就是懒惰标记,即线面代码中的 lazy[]数组,因为我们主要想求的是某一区间的总和,而对该区间内每一个离散点的具体值没有兴趣,所以,当我们进行区间修改的时候,只需要修改该区间所对应节点的

基本线段树模板(建树、点/区间修改、查询)

线段树主要用于区间记录信息(如区间和.最大最小值等),首先是建树: 这里以求和为例: 1 const int MAXM=50000; //定义 MAXM 为线段最大长度 2 3 int a[MAXM+5],segtree[(MAXM<<2)+5]; // a 数组为 main 函数中读入的内容,segtree 数组为需要查询的数的信息(如和.最值等),树的空间大小为线段最大长度的四倍 4 5 void build(int o,int l,int r){ //传入的参数为 o:当前需要建立的结点

线段树区间修改 P3372 【模板】线段树 1

题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.求出某区间每一个数的和 输入输出格式 输入格式: 第一行包含两个整数N.M,分别表示该数列数字的个数和操作的总个数. 第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值. 接下来M行每行包含3或4个整数,表示一个操作,具体如下: 操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k 操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和 输出格式: 输出包含若干行整