题意:

题解:典型伸展树的题,比较全面。

我理解的伸展树:

1 伸展操作:就是旋转,因为我们只需保证二叉树中序遍历的结果不变,所以我们可以旋转来保持树的平衡,且旋转有左旋与右旋。通过这种方式保证不会让树一直退化从而超时。虽然一次旋转的代价比较高,但是可以证明:每次操作都旋转(关键),则时间复杂度为O(n*log2 n)

2 更新:每个节点都可以存一些信息,并模拟线段树进行区间操作。父节点的信息是两个孩子节点加当前父节点的信息的总和。因为是可旋转的搜索二叉树,所以每次处理都需要注意上更新或下更新

3 注意:一般需要先开两个哨兵节点,一个作为开头,有个作为结尾,这样可以避免一些边界的讨论问题

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define dir(a,b) (a>>b)
#define ssplay(rt,x) (splay[rt].chd[x])
typedef long long ll;
const int Max=6e5+;
const int Inf=<<;
int num[Max],memp[Max],tot2,tot,root;//存值 内存池 内存池的值 总值 根节点
struct node
{
int file,sam;//翻转 是否修改
int chd[],fat;
int sizee,sum,val;//总个数 总大小 值
int lmax,rmax,mmax;//区间合并三变量
} splay[Max]; void Treaval(int rt)
{
if(rt)
{
Treaval(splay[rt].chd[]);
printf("rt=%2d lchd=%2d rchd=%2d sum=%2d size=%2d val=%2d lmax=%2d rmax=%2d mmax=%2d\n",rt,splay[rt].chd[],splay[rt].chd[],
splay[rt].sum,splay[rt].sizee,splay[rt].val,splay[rt].lmax,splay[rt].rmax,splay[rt].mmax);
Treaval(splay[rt].chd[]);
}
return;
}
void debug()
{
printf("root=%d\n",root);
Treaval(root);
return;
} inline void NewNode(int &rt,int fa,int va)//建立新节点
{
if(tot2)//删除后的内存池可以再次利用
rt=memp[tot2--];
else
rt=++tot;
splay[rt].file=splay[rt].sam=;
splay[rt].chd[]=splay[rt].chd[]=;
splay[rt].val=splay[rt].lmax=splay[rt].rmax=splay[rt].mmax=splay[rt].sum=va;
splay[rt].fat=fa;
return;
} inline int nmax(int a,int b)
{
return a>b?a:b;
}
inline void PushUp(int rt)//上更新(类似区间合并)
{
int lchd=splay[rt].chd[],rchd=splay[rt].chd[];
splay[rt].sizee=splay[lchd].sizee+splay[rchd].sizee+;
splay[rt].sum=splay[lchd].sum+splay[rchd].sum+splay[rt].val; splay[rt].mmax=nmax(nmax(splay[rt].val,splay[lchd].mmax),splay[rchd].mmax);//处理区间最大值
if(splay[rt].mmax>)
splay[rt].mmax=nmax(splay[rt].mmax,nmax(splay[lchd].rmax,)+nmax(splay[rchd].lmax,)+splay[rt].val);//val为关键 splay[rt].lmax=nmax(splay[lchd].lmax,splay[lchd].sum+splay[rt].val);
splay[rt].lmax=nmax(splay[rt].lmax,splay[lchd].sum+splay[rt].val+splay[rchd].lmax); splay[rt].rmax=nmax(splay[rchd].rmax,splay[rchd].sum+splay[rt].val);
splay[rt].rmax=nmax(splay[rt].rmax,splay[rchd].sum+splay[rt].val+splay[lchd].rmax);
return;
}
inline void Swap(int &a,int &b)
{
int t=a;
a=b;
b=t;
return;
}
inline void fson(int rt)//翻转
{
if(!rt)
return;
Swap(splay[rt].chd[],splay[rt].chd[]);//孩子交换就好
Swap(splay[rt].lmax,splay[rt].rmax);//此位置的左右max需交换
splay[rt].file^=;//此处修改与否只与父节点flie有关,与此处的file无关
}
inline void sson(int rt,int va)//修改成va
{
if(!rt)
return;
splay[rt].val=va;
splay[rt].sum=va*splay[rt].sizee;
splay[rt].lmax=splay[rt].rmax=splay[rt].mmax=nmax(splay[rt].sum,va);
splay[rt].sam=;
}
inline void PushDown(int rt)//下更新(处理翻转与改变值)
{
if(splay[rt].file)
{
fson(splay[rt].chd[]);
fson(splay[rt].chd[]);
splay[rt].file=;
}
if(splay[rt].sam)
{
sson(splay[rt].chd[],splay[rt].val);
sson(splay[rt].chd[],splay[rt].val);
splay[rt].sam=;
}
return;
} inline void Rotate(int rt,int kind)//**zig或者zag**
{
int y=splay[rt].fat;
PushDown(y);
PushDown(rt);
splay[y].chd[kind^]=splay[rt].chd[kind];
splay[ssplay(rt,kind)].fat=y;
if(splay[y].fat)//不是一个zig后者zag
splay[splay[y].fat].chd[ssplay(splay[y].fat,)==y]=rt;//y父节点的(y的左右)孩子
splay[rt].fat=splay[y].fat;
splay[rt].chd[kind]=y;
splay[y].fat=rt;
PushUp(y);
return;
}
inline void Splay(int rt,int goal)//**关键的伸展操作(双旋)**
{
PushDown(rt);
while(splay[rt].fat!=goal)
{
int y=splay[rt].fat;
if(splay[y].fat==goal)//一次zig/zag
{
Rotate(rt,splay[y].chd[]==rt);//rt是否为左孩子
}
else
{
int kind=(splay[splay[y].fat].chd[]==y?:);//y是否为左孩子
if(splay[y].chd[kind]==rt)//左孩子的右孩子或者右孩子的左孩子
{
Rotate(rt,kind^);
Rotate(rt,kind);
}
else
{
Rotate(y,kind);
Rotate(rt,kind);
}
}
}
PushUp(rt);
if(!goal)
root=rt;//更新根节点
return;
}
inline void Rotateto(int pos,int goal)//**得到第pos个数,并且进行伸展**
{
int rt=root;
PushDown(rt);
while(splay[ssplay(rt,)].sizee!=pos)
{
if(splay[ssplay(rt,)].sizee>pos)
rt=splay[rt].chd[];
else
{
pos-=(splay[ssplay(rt,)].sizee+);
rt=splay[rt].chd[];
}
PushDown(rt);
}
Splay(rt,goal);
return;
} void Create(int sta,int enn,int &rt,int fa)//建树与添树
{
if(sta>enn)
return;
int mid=dir(sta+enn,);
NewNode(rt,fa,num[mid]);
Create(sta,mid-,splay[rt].chd[],rt);
Create(mid+,enn,splay[rt].chd[],rt);
PushUp(rt);//建树与添树时上更新
return;
}
void Init(int n)//初始化
{
for(int i=; i<n; ++i)
scanf("%d",&num[i]);
splay[].lmax=splay[].rmax=splay[].mmax=-Inf;//可能全为负数
splay[].chd[]=splay[].chd[]=splay[].fat=;//建立哨兵,避免特判
splay[].val=splay[].sizee=splay[].sum=tot2=tot=root=;
splay[].sam=splay[].file=;
NewNode(root,,);//建立两个哨兵
NewNode(splay[root].chd[],root,);
Create(,n-,splay[ssplay(root,)].chd[],splay[root].chd[]);
PushUp(splay[root].chd[]);//与建树一起的上更新
PushUp(root);
return;
}
void Insert(int pos,int dig)//在pos与pos+1之间添加
{
Rotateto(pos,);//旋转pos位置的值成0的孩子
Rotateto(pos+,root);
Create(,dig-,splay[ssplay(root,)].chd[],splay[root].chd[]);//建立
PushUp(splay[root].chd[]);//更新
PushUp(root);//下面很多函数都是五行中仅仅修改第三行
return;
} void Erase(int rt)//回收空间
{
if(!rt)
return;
memp[++tot2]=rt;
Erase(splay[rt].chd[]);
Erase(splay[rt].chd[]);
return;
}
void Delete(int pos,int dig)//删除pos后面dig个
{
Rotateto(pos-,);
Rotateto(pos+dig,root);
Erase(splay[ssplay(root,)].chd[]);//关键位置
splay[ssplay(root,)].chd[]=;
PushUp(splay[root].chd[]);
PushUp(root);
return;
} void Make_same(int pos,int dig,int fix)//修改pos后面dig和为fix
{
Rotateto(pos-,);
Rotateto(pos+dig,root);
sson(splay[ssplay(root,)].chd[],fix);
PushUp(splay[root].chd[]);
PushUp(root);
return;
} void Reverse(int pos,int dig)//翻转pos后面dig个
{
Rotateto(pos-,);
Rotateto(pos+dig,root);
fson(splay[ssplay(root,)].chd[]);
PushUp(splay[root].chd[]);
PushUp(root);
return;
} int GetSum(int pos,int dig)//计算pos后面dig个数的和
{
Rotateto(pos-,);
Rotateto(pos+dig,root);
return splay[ssplay(ssplay(root,),)].sum;
} int GetMaxsum(int pos,int dig)//区间最值
{
Rotateto(pos-,);
Rotateto(pos+dig,root);
return splay[ssplay(ssplay(root,),)].mmax;//注意每次都需要splay一下,保证时间复杂度
} int main()
{
int n,m;
char str[];
int pos,dig,fix;
while(~scanf("%d %d",&n,&m))
{
Init(n);
while(m--)
{
//debug();
scanf("%s",str);
if(!strcmp(str,"INSERT"))//添加一段数
{
scanf("%d %d",&pos,&dig);
for(int i=; i<dig; ++i)
scanf("%d",&num[i]);
Insert(pos,dig);
}
else if(!strcmp(str,"DELETE"))//删除一段数
{
scanf("%d %d",&pos,&dig);
Delete(pos,dig);
}
else if(!strcmp(str,"MAKE-SAME"))//修改
{
scanf("%d %d %d",&pos,&dig,&fix);
Make_same(pos,dig,fix);
}
else if(!strcmp(str,"REVERSE"))//翻转
{
scanf("%d %d",&pos,&dig);
Reverse(pos,dig);
}
else if(!strcmp(str,"GET-SUM"))//求和
{
scanf("%d %d",&pos,&dig);
printf("%d\n",GetSum(pos,dig));
}
else//最大子序列
{
printf("%d\n",GetMaxsum(,splay[root].sizee-));//注意有两个哨兵
}
}
}
return ;
}

HYSBZ 1500 维修数列(伸展树模板)的更多相关文章

  1. [BZOJ 1500]维修数列 [Splay Tree从进阶到住院]

    历尽艰辛终于A掉了这题QwQ 贴COGS评论区几句话=.= 策爷:"splay/块状链表的自虐题.".深刻理解到如果没有M倾向就不要去写这题了.. -Chenyao2333 记得b ...

  2. bzoj 1500 维修数列

    splay乱搞. 调了两个多小时...这辈子再也不想写splay了... 维护左边最大连续和右边最大连续,维护两个标记,无脑push_down.push_up就行了. 注意最大连续和至少要包含一个数. ...

  3. BZOJ 1500 维修数列【Splay】

    注意:1,内存限制,所以需要回收删除的点 2,当前节点的左连续区间和最大值=max(左子树的左连续区间和最大值,左子树的总和+当节点的值+max(右子树的左连续区间和最大值,0)):右连续区间和最大值 ...

  4. splay伸展树模板

      普通版本: struct SplayTree { ; ], key[maxn], val[maxn], sz[maxn], lz[maxn], fa[maxn]; , ) { ch[x][]=ch ...

  5. 【BZOJ】1500: [NOI2005]维修数列

    [算法]splay [题解]数据结构 感谢Occult的模板>_<:HYSBZ 1500 维修数列 #include<cstdio> #include<cctype> ...

  6. wikioi 1396 伸展树(两个模板)

    题目描写叙述 Description Tiger近期被公司升任为营业部经理.他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司的账本,账本上记录了公司成立以来 ...

  7. bzoj 1500: [NOI2005]维修数列 splay

    1500: [NOI2005]维修数列 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 6556  Solved: 1963[Submit][Status ...

  8. BZOJ 1500: [NOI2005]维修数列 (splay tree)

    1500: [NOI2005]维修数列 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 4229  Solved: 1283[Submit][Status ...

  9. Splay伸展树入门(单点操作,区间维护)附例题模板

    Pps:终于学会了伸展树的区间操作,做一个完整的总结,总结一下自己的伸展树的单点操作和区间维护,顺便给未来的自己总结复习用. splay是一种平衡树,[平均]操作复杂度O(nlogn).首先平衡树先是 ...

随机推荐

  1. 《从零开始学Swift》学习笔记(Day 51)——扩展构造函数

    原创文章,欢迎转载.转载请注明:关东升的博客 扩展类型的时候,也可以添加新的构造函数.值类型与引用类型扩展有所区别.值类型包括了除类以外的其他类型,主要是枚举类型和结构体类型. 值类型扩展构造函数 扩 ...

  2. 获取字符串已utf-8表示的字节数

    private static int utf8Length(String string) { /** Returns the number of bytes required to write thi ...

  3. Farm Tour(最小费用最大流模板)

    Farm Tour Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 18150   Accepted: 7023 Descri ...

  4. What are DESC and ASC Keywords?

    What are DESC and ASC Keywords? ASC is the short form for ascending DESC is the short form for desce ...

  5. win8内置管理员账号既能使用metro应用又能使用默认共享

    一.内置管理员账号打开METRO应用 1.按WIN+R 弹出运行框. 2.输入gpedit.msc点确定,打开本地组策略编辑器. 3.逐级选择“计算机配置”,“Windows 设置”,“安全设置”,“ ...

  6. Python中的魔术(双下划线'__xxx__')方法详解

    介绍 在Python中,所有以“__”双下划线包起来的方法,都统称为“Magic Method”,中文称『魔术方法』,例如类的初始化方法 __init__ ,Python中所有的魔术方法均在官方文档中 ...

  7. 正确使用索引(sql优化),limit分页优化,执行计划,慢日志查询

    查看表相关命令 - 查看表结构   desc 表名- 查看生成表的SQL   show create table 表名- 查看索引   show index from  表名 使用索引和不使用索引 由 ...

  8. boost之字符串与文本处理

    C++标准库中字符串转数值使用函数atoi(),数值转字符串使用printf系列函数. boost中使用转换函数操作符lexical_cast<>进行转换,实际上是模板函数.自定义类型,要 ...

  9. boost之内存池

    讲到内存池我们会想到对对象进行动态分配的过程new包含三个过程 1.使用operator new分配内存 2.使用placement new 初始化 3.返回内存地址. 分配内存可以分解成分配内存和获 ...

  10. 《UNI|X环境高级编程》 源代码配置

    代码下载地址:http://www.apuebook.com/ 下的第二版,里面有个readme文件: root@iZ23onhpqvwZ:~/ms/linux/apue/apue.2e# cat R ...