hdu1890 伸展树(区间反转)

对于大神来说这题是水题。我搞这题花了快2天。

  伸展树的优点有什么,就是树不管你怎么旋转序列是不会改变得,并且你要使区间反转,只要把第k大的点转到根结点,那么它的左子树就是要交换的区间[l,r),然后交换左右

子树就可以了(中序),根结点的位置就是i+siz[ch[root][0]],i是处理完的结点个数,siz[ch[root][0]]就是左子树(需要旋转的个数)。 旋转可以用lazy思想标记,这样时间就为logn了。由于第k大的值已经处理完成,所以直接将根结点删除。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define key_value ch[ch[root][1]][0]
using namespace std;
const int MAXN = 100010;
struct node
{
    int id;
    int v;
}a[MAXN];
int pre[MAXN],ch[MAXN][2],siz[MAXN],lazy[MAXN],tot1,root;
int s[MAXN],tot2,n;
bool cmp(node fa,node fb)
{
    if(fa.v != fb.v)
        return fa.v < fb.v;
    return fa.id < fb.id;
}
void Treavel(int x)
{
    if(x)
    {
        Treavel(ch[x][0]);
        printf("结点%2d:左儿子 %2d 右儿子 %2d 父结点 %2d size=%2d,lazy=%2d\n",x,ch[x][0],ch[x][1],pre[x],siz[x],lazy[x]);
        Treavel(ch[x][1]);
    }
}
void debug()
{
    printf("root:%d\n",root);
    Treavel(root);
}
void Newnode(int &rt,int pa,int k)
{
    rt = k;
    pre[rt] = pa;
    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;
}
void pushdown(int rt)
{
    if(lazy[rt]){
        lazy[ch[rt][0]] ^= 1;
        lazy[ch[rt][1]] ^= 1;
        swap(ch[rt][0],ch[rt][1]);
        lazy[rt] = 0;
    }
}
void build(int &rt,int l,int r,int pa)
{
    if(l > r){
        return ;
    }
    int m = (l+r)/2;
    Newnode(rt,pa,m);
    build(ch[rt][0],l,m-1,rt);
    build(ch[rt][1],m+1,r,rt);
    pushup(rt);
}
void Init()
{
    root = tot1 = tot2 = 0;
    ch[root][0] = ch[root][1] = siz[root] = lazy[root] = pre[root] = 0;
    build(root,1,n,0);
    pushup(root);
}
void Rotate(int rt,int kind)
{
    pushdown(pre[rt]);
    pushdown(rt);
    int y = pre[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);
    pushup(rt);
}
void splay(int rt,int goal)
{
    pushdown(rt);
    while(pre[rt] != goal)
    {
        if(pre[pre[rt]] == goal){
            pushdown(pre[rt]);
            pushdown(rt);
            Rotate(rt,ch[pre[rt]][0]==rt);
        }
        else {
            pushdown(pre[pre[rt]]);
            pushdown(pre[rt]);
            pushdown(rt);
            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_max(int rt)
{
    pushdown(rt);
    while(ch[rt][1]){
        rt = ch[rt][1];
        pushdown(rt);
    }
    return rt;
}
void del(int root)
{
    if(ch[root][0] == 0){
        root = ch[root][1];
        pre[root] = 0;
    }
    else {
        int t = Get_max(ch[root][0]);
        splay(t,root);
        ch[t][1] = ch[root][1];
        pre[ch[root][1]] = t;
        root = t;
        pre[root] = 0;
        pushup(root);
    }
}
int main()
{
    int i;
    while(~scanf("%d",&n),n)
    {
        for(i=1; i<=n; i++){
            scanf("%d",&a[i].v);
            a[i].id = i;
        }
        sort(a+1,a+n+1,cmp);
        Init();

        for(i=1; i<=n; i++){
            //cout<<"test1: "<<endl;

            splay(a[i].id,0);
           // debug();
            lazy[ch[root][0]] ^= 1;
            printf("%d",i+siz[ch[root][0]]);
            if(i < n)printf(" ");
            else printf("\n");
            del(root);

        }
    }
}
时间: 2024-12-26 14:47:17

hdu1890 伸展树(区间反转)的相关文章

数据结构之伸展树

1. 概述 二叉查找树(Binary Search Tree,也叫二叉排序树,即Binary Sort Tree)能够支持多种动态集合操作,它可以用来表示有序集合.建立索引等,因而在实际应用中,二叉排序树是一种非常重要的数据结构. 从算法复杂度角度考虑,我们知道,作用于二叉查找树上的基本操作(如查找,插入等)的时间复杂度与树的高度成正比.对一个含n个节点的完全二叉树,这些操作的最坏情况运行时间为O(log n).但如果因为频繁的删除和插入操作,导致树退化成一个n个节点的线性链(此时即为一个单链表

HDU 1890 Robotic Sort 伸展树的区间反转与延迟标记

延迟标记像极了线段树,不再多说. 区间反转在树伸展到位之后,也变成了简单的递归交换左右儿子. 愈发感觉到伸展树简直太漂亮了,伸展操作更是诱惑到不行 ,总之数据结构太有魅力了. 比较简单,就直接上模板了. #include <algorithm> #include <iostream> #include <cstring> #include <cstdlib> #include <cstdio> #include <queue> #in

hdu 1754 splay tree伸展树 初战(单点更新,区间属性查询)

题意:与区间查询点更新,点有20W个,询问区间的最大值.曾经用线段树,1000+ms,今天的伸展树,890没ms,差不多. 第一次学习伸展树,一共花了2个单位时间,感觉伸展树真很有用,也很好玩.现在只学了一点点.切个点更新试试. 大致思路:用编号(数组)作为树的键值建树,每插一个数,沿路节点更新最大值(每个结点有一个附加信息标记以之为子树的树所有点的最大值).所以,查询时[i,j],只要把i-1伸展到树根,把j+1伸展到I-1下面,那么j+1的左子树就是要的区间了!查该子树根值信息即可(特判端点

Splay伸展树入门(单点操作,区间维护)

ps:终于学会了伸展树的区间操作,做一个完整的总结,总结一下自己的伸展树的单点操作和区间维护,顺便给未来的总结复习用. splay是一种平衡树,[平均]操作复杂度O(nlogn).首先平衡树先是一颗二叉搜索树,刚刚开始学的时候找题hash数字的题先测板子... 后来那题被学长改了数据不能用平衡树测了...一道二分数字的题. 二叉搜索树的功能是,插入一个数字,在O(logn)的时间内找到它,并操作,插入删除等.但是可能会让二叉搜索树退化成链,复杂度达到O(n) 原文地址:https://www.c

poj_3580 伸展树

自己伸展树做的第一个题 poj 3580 supermemo. 题目大意 对一个数组进行维护,包含如下几个操作: ADD x, y, d 在 A[x]--A[y] 中的每个数都增加d REVERSE x, y 将 A[x]--A[y] 中的数进行反转,变为 A[y],A[y-1]....A[x+1],A[x] REVOLVE x, y, T 将 A[x]--A[y]中的数连续右移T次 INSERT x, P 在A后添加数P DELETE x 删除A[x] MIN x, y 查询A[x]--A[y

算法模板——splay区间反转 2

实现功能:同splay区间反转 1(基于BZOJ3223 文艺平衡树) 这次改用了一个全新的模板(HansBug:琢磨了我大半天啊有木有),大大简化了程序,同时对于splay的功能也有所完善 这里面没有像一般二叉排序树那样子用一个参量进行排序,而是直接以中序遍历来构建了一个普通的二叉树(当然也可以把每个点的中序遍历排名视作参量),然后插入的时候就是指定位置插入(这个就比较像是文本插入了) 总之得到了较大的提升,代码优美程度也提高不少 1 var 2 i,j,k,l,m,n,head,tot,ll

HDU 1890 - Robotic Sort - [splay][区间反转+删除根节点]

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1890 Time Limit: 6000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Problem Description Somewhere deep in the Czech Technical University buildings, there are laboratories for examining

poj_3468 伸展树

题目大意 一个数列,每次操作可以是将某区间数字都加上一个相同的整数,也可以是询问一个区间中所有数字的和.(这里区间指的是数列中连续的若干个数)对每次询问给出结果. 思路 1. 伸展树的一般规律 对于区间的查找更新操作,可以考虑使用伸展树.线段树等数据结构.这里使用伸展树来解决.     伸展树对数组进行维护的核心思想是,将需要维护的一组数单独提取出来,形成一棵子树(一般为整棵树的根节点的右子节点的左孩子节点 为根),然后再这个子树上进行操作.此时进行某些操作(如 ADD, SUM 等),只需要在

伸展树

伸展树(Splay Tree)树平衡二叉查找树的一种,具有二叉查找树的所有性质.在性能上又比普通的二叉查找树有所改进:普通的二叉查找树在最坏情况下的查找操作的时间复杂度为O(n)(当二叉树退化成一条链的时候),而伸展树在任何情况下的平摊时间复杂度均为 O(log2n). 特性 和普通的二叉查找树相比,具有任何情况下.任何操作的平摊O(log2n)的复杂度,时间性能上更好 和一般的平衡二叉树比如 红黑树.AVL树相比,维护更少的节点额外信息,空间性能更优,同时编程复杂度更低 在很多情况下,对于查找