[NOI2005]维修数列 Splay tree 区间反转,修改,求和,求最值
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1500
Description
Input
Output
Sample Input
2 -6 3 5 1 -5 -3 6 3
GET-SUM 5 4
MAX-SUM
INSERT 8 3 -5 7 2
DELETE 12 1
MAKE-SAME 3 3 2
REVERSE 3 6
GET-SUM 5 4
MAX-SUM
Sample Output
10
1
10
HINT
反转和求和操作很好求,直接用两个标记rev和sum懒惰求值。对于区间最大值,每个区间需要维护3个信息:左端点开始的最大值,右端点的最大值,区间最大值。Push_Up
根据这三个值来就可以了,很好推。。。
TlE+WA很多次,有很多要注意的地方。。。
首先,Insert操作有很多,会使用过多的空间(大概100MB),如果不回收空间,会超时,我们可以人工压一个栈回收删除的节点。。。
在初始化根节点的虚拟父亲节点0的时候,sum初始化为0,maxl、maxm和maxr需要初始化为-INF,因为如果一个节点没有两个儿子,那么会通过0节点来更新。
Update_Same操作要注意如果节点不存在则不要更新,不然会影响0号节点。rev操作不仅仅要交换左右节点,还要交换左右最值。。。
因为这里涉及反转和求最值操作,因此反转操作的延迟更新需要快于求最值的更新。。。
基本这些问题都注意了,就差不多了。。。
我开始在求MAX-SUM的时候是把初始增加的两个节点val初始化为0,然后用Push_Up维护更新信息,那么直接输出maxm[root]就可以了,但是WA了T^T。。。但是改为Splay()来维护,然后输出maxm[Key_value]就能A了(见代码上的注释),郁闷得死啊,求大神解释><..
//STATUS:C++_AC_6060MS_23736KB
#include <functional>
#include <algorithm>
#include <iostream>
//#include <ext/rope>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <numeric>
#include <cstring>
#include <cassert>
#include <cstdio>
#include <string>
#include <vector>
#include <bitset>
#include <queue>
#include <stack>
#include <cmath>
#include <ctime>
#include <list>
#include <set>
#include <map>
using namespace std;
//using namespace __gnu_cxx;
//define
#define pii pair<int,int>
#define mem(a,b) memset(a,b,sizeof(a))
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define PI acos(-1.0)
//typedef
//typedef __int64 LL;
//typedef unsigned __int64 ULL;
//const
const int N=;
const int INF=0x3f3f3f3f;
const int MOD=,STA=;
//const LL LNF=1LL<<60;
const double EPS=1e-;
const double OO=1e15;
const int dx[]={-,,,};
const int dy[]={,,,-};
const int day[]={,,,,,,,,,,,,};
//Daily Use ...
inline int sign(double x){return (x>EPS)-(x<-EPS);}
template<class T> T gcd(T a,T b){return b?gcd(b,a%b):a;}
template<class T> T lcm(T a,T b){return a/gcd(a,b)*b;}
template<class T> inline T lcm(T a,T b,T d){return a/d*b;}
template<class T> inline T Min(T a,T b){return a<b?a:b;}
template<class T> inline T Max(T a,T b){return a>b?a:b;}
template<class T> inline T Min(T a,T b,T c){return min(min(a, b),c);}
template<class T> inline T Max(T a,T b,T c){return max(max(a, b),c);}
template<class T> inline T Min(T a,T b,T c,T d){return min(min(a, b),min(c,d));}
template<class T> inline T Max(T a,T b,T c,T d){return max(max(a, b),max(c,d));}
//End #define Key_value ch[ch[root][1]][0]
int pre[N],ch[N][]; //分别表示父结点,键值,左右孩子(0为左孩子,1为右孩子),根结点,结点数量
int sz[N],st[N]; //子树规模,内存池
int root,tot,top; //根节点,根节点数量,内存池容量
//题目特定数目
int val[N],sum[N],maxl[N],maxm[N],maxr[N],num[N];
bool rev[N],flag[N];
int n,m,posi,all;
//debug部分copy from hh
void Treaval(int x) {
if(x) {
Treaval(ch[x][]);
printf("结点%2d:val = %2d 左儿子 %2d 右儿子 %2d 父结点 %2d size = %2d maxm = %2d %2d %2d\n",x,val[x],ch[x][],ch[x][],pre[x],sz[x],maxm[x],maxl[x],maxr[x]);
Treaval(ch[x][]);
}
}
void debug() {printf("%d\n",root);Treaval(root);}
//以上Debug
//新建一个结点
void NewNode(int &x,int fa,int a)
{
if(top)x=st[top--];
else x=++tot;
sum[x]=val[x]=maxl[x]=maxm[x]=maxr[x]=a;
pre[x]=fa;
flag[x]=rev[x]=;
ch[x][]=ch[x][]=; //左右孩子为空
}
void Update_Same(int x,int v){
if(!x) return;
flag[x]=;
val[x]=v;
sum[x]=sz[x]*v;
maxl[x]=maxr[x]=maxm[x]=max(v,v*sz[x]);
}
void Update_Rev(int x){
if(!x) return;
swap(ch[x][],ch[x][]);
swap(maxl[x],maxr[x]); //翻转时左右区间也交换!!!!
rev[x]^=;
}
void Push_Up(int x)
{
int ls=ch[x][],rs=ch[x][];
sz[x]=sz[ls]+sz[rs]+;
sum[x]=sum[ls]+sum[rs]+val[x];
maxl[x]=Max(maxl[ls],sum[ls]+val[x]+Max(,maxl[rs]));
maxr[x]=Max(maxr[rs],sum[rs]+val[x]+Max(,maxr[ls]));
maxm[x]=Max(maxr[ls],)+val[x]+Max(maxl[rs],);
maxm[x]=Max(maxm[x],maxm[ls],maxm[rs]);
} void Push_Down(int x)
{
if(flag[x]){
Update_Same(ch[x][],val[x]);
Update_Same(ch[x][],val[x]);
flag[x]=;
}
if(rev[x]){
Update_Rev(ch[x][]);
Update_Rev(ch[x][]);
rev[x]=;
}
}
//旋转,kind为1为右旋,kind为0为左旋
void Rotate(int x,int kind)
{
int y=pre[x],z=pre[y];
Push_Down(y);
Push_Down(x); //先把y的标记向下传递,再把x的标记往下传递
//类似SBT,要把其中一个分支先给父节点
ch[y][!kind]=ch[x][kind];
pre[ch[x][kind]]=y;
//如果父节点不是根结点,则要和父节点的父节点连接起来
if(z)ch[z][ch[z][]==y]=x;
pre[x]=z;
ch[x][kind]=y;
pre[y]=x;
Push_Up(y); //维护y结点,不要维护x节点,x节点会再次Push_Down,最后维护一下x节点即可
}
//Splay调整,将根为r的子树调整为goal
void Splay(int x,int goal)
{
int y,z,kind;
while(pre[x]!=goal){
//父节点即是目标位置,goal为0表示,父节点就是根结点
y=pre[x];
Push_Down(pre[y]);Push_Down(y);Push_Down(x); //设计到反转操作,要先更新,然后在判断!!
if(pre[y]==goal){
Rotate(x,ch[y][]==x);
}
else {
kind=ch[pre[y]][]==y;
//两个方向不同,则先左旋再右旋
if(ch[y][kind]==x){
Rotate(x,!kind);
Rotate(x,kind);
}
//两个方向相同,相同方向连续两次
else {
Rotate(y,kind);
Rotate(x,kind);
}
}
}
//更新根结点
Push_Up(x);
if(goal==)root=x;
} void RotateTo(int k,int goal)
{
int x=root;
Push_Down(x);
while(sz[ch[x][]]!=k){
if(sz[ch[x][]]>k)
x=ch[x][];
else {
k-=sz[ch[x][]]+;
x=ch[x][];
}
Push_Down(x);
}
Splay(x,goal);
}
//建树,中间结点先建立,然后分别对区间两端在左右子树建立
void BuildTree(int &x,int l,int r,int fa)
{
if(l>r)return;
int mid=(l+r)>>;
NewNode(x,fa,num[mid]);
BuildTree(ch[x][],l,mid-,x);
BuildTree(ch[x][],mid+,r,x);
Push_Up(x);
} void Init()
{
root=top=tot=;
ch[][]=ch[][]=sz[]=pre[]=;
val[]=rev[]=sum[]=flag[]=;
maxl[]=maxm[]=maxr[]=-INF; for(int i=;i<n;i++)
scanf("%d",num+i);
NewNode(root,,);
NewNode(ch[root][],root,);
BuildTree(Key_value,,n-,ch[root][]);
Push_Up(ch[root][]);
Push_Up(root);
} void Insert()
{
RotateTo(posi,);
RotateTo(posi+,root);
BuildTree(Key_value,,all-,ch[root][]);
Push_Up(ch[root][]);
Push_Up(root);
} void erase(int r){
if(!r) return;
st[++top]=r;
erase(ch[r][]);
erase(ch[r][]);
} void Delete()
{
RotateTo(posi-,);
RotateTo(posi+all,root);
erase(Key_value);
pre[Key_value]=;
Key_value=;
Push_Up(ch[root][]);
Push_Up(root);
} void Update(int same)
{
RotateTo(posi-,);
RotateTo(posi+all,root);
int x=Key_value;
if(x==)return;
flag[x]=;
val[x]=same;
sum[x]=sz[x]*same;
maxl[x]=maxm[x]=maxr[x]=Max(same,same*sz[x]);
Push_Up(ch[root][]);
Push_Up(root);
} void Reverse()
{
RotateTo(posi-,);
RotateTo(posi+all,root);
Update_Rev(Key_value);
/* Push_Up维护 Wa...
Push_Up(ch[root][1]);
Push_Up(root); */
} int main()
{
// freopen("in.txt","r",stdin);
int i,j,k,a;
char op[];
while(~scanf("%d%d",&n,&m))
{
Init();
while(m--){
scanf("%s",op);
if(op[]=='I'){
scanf("%d%d",&posi,&all);
for(i=;i<all;i++)
scanf("%d",num+i);
Insert();
}
else if(op[]=='D'){
scanf("%d%d",&posi,&all);
Delete();
}
else if(op[]=='K'){
scanf("%d%d%d",&posi,&all,&a);
Update(a);
}
else if(op[]=='R'){
scanf("%d%d",&posi,&all);
Reverse();
}
else if(op[]=='G'){
scanf("%d%d",&posi,&all);
RotateTo(posi-,);
RotateTo(posi+all,root);
printf("%d\n",sum[Key_value]);
}
else {
RotateTo(,);
RotateTo(sz[root]-,root);
printf("%d\n",maxm[Key_value]);
/* 如果Push_Up维护,那么不需要上面的Totate()来维护 wa...
printf("%d\n",maxm[root]); */
}
}
}
return ;
}
[NOI2005]维修数列 Splay tree 区间反转,修改,求和,求最值的更多相关文章
- BZOJ 1500: [NOI2005]维修数列 (splay tree)
1500: [NOI2005]维修数列 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 4229 Solved: 1283[Submit][Status ...
- BZOJ1500 [NOI2005]维修数列(Splay tree)
[Submit][Status][Discuss] Description 请写一个程序,要求维护一个数列,支持以下 6 种操作: 请注意,格式栏 中的下划线‘ _ ’表示实际输入文件中的空格 Inp ...
- [BZOJ 1500]维修数列 [Splay Tree从进阶到住院]
历尽艰辛终于A掉了这题QwQ 贴COGS评论区几句话=.= 策爷:"splay/块状链表的自虐题.".深刻理解到如果没有M倾向就不要去写这题了.. -Chenyao2333 记得b ...
- 【BZOJ1500】[NOI2005]维修数列 Splay
[BZOJ1500][NOI2005]维修数列 Description Input 输入的第1 行包含两个数N 和M(M ≤20 000),N 表示初始时数列中数的个数,M表示要进行的操作数目.第2行 ...
- bzoj 1500: [NOI2005]维修数列 splay
1500: [NOI2005]维修数列 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 6556 Solved: 1963[Submit][Status ...
- [bzoj1500][NOI2005 维修数列] (splay区间操作)
Description Input 输入的第1 行包含两个数N 和M(M ≤20 000),N 表示初始时数列中数的个数,M表示要进行的操作数目. 第2行包含N个数字,描述初始时的数列. 以下M行,每 ...
- BZOJ1500: [NOI2005]维修数列[splay ***]
1500: [NOI2005]维修数列 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 12278 Solved: 3880[Submit][Statu ...
- [bzoj1500][NOI2005]维修数列——splay
题目 题解 这道题可以说是数列问题的大BOSS,也算是这一周来学习splay等数据结构的一个总结. 我们一个一个地看这些操作. 对于操作1,我们首先建一棵子树,直接接上原树即可. 对于操作2,我们找到 ...
- BZOJ1500: [NOI2005]维修数列 [splay序列操作]【学习笔记】
以前写过这道题了,但我把以前的内容删掉了,因为现在感觉没法看 重写! 题意: 维护一个数列,支持插入一段数,删除一段数,修改一段数,翻转一段数,查询区间和,区间最大子序列 splay序列操作裸题 需要 ...
随机推荐
- Error building Player: CommandInvokationFailure: Failed to re-package resources. See the Console for details. ShareSDK 也有这种错误
Error building Player: CommandInvokationFailure: Failed to re-package resources. See the Console for ...
- 跨平台Unicode与UTF8互转代码
参考来源:http://blog.csdn.net/flying8127/article/details/1598521 在原来原基础上,将代码整理,并加强安全性. 并按照WindowsAPI设计, ...
- 如何在WINDOWS下编译BOOST C++库 .
如何在WINDOWS下编译BOOST C++库 cheungmine 2008-6-25 写出来,怕自己以后忘记了,也为初学者参考.使用VC8.0和boost1.35.0. 1)下载boost ...
- HDU 1757 A Simple Math Problem(矩阵快速幂)
题目链接 题意 :给你m和k, 让你求f(k)%m.如果k<10,f(k) = k,否则 f(k) = a0 * f(k-1) + a1 * f(k-2) + a2 * f(k-3) + …… ...
- c缺陷与陷阱笔记-第二章 语法陷阱
1.函数的调用和番薯返回值是函数指针的声明 定义一个函数指针,例如 int (*fp)(float),这个函数的返回值是Int,参数是1个float类型,调用这个函数的方法是 (*fp)(),还有f ...
- JNI和NDK的区别
http://blog.csdn.net/ithomer/article/details/6828830 NDK(Native Development Kit)“原生”也就是二进制 android常用 ...
- 通过web代理进行跨域访问,http请求返回超时的问题定位
[现象] 在ajax通过web代理跨域访问时,http第一次登陆时正常,但是第二次再下发其他命令的时候总是返回 java.net.SocketTimeoutException: Read timed ...
- java:比较对象
对象内容相等条件:1.对象类型相同(可用instanceof操作符比较)2.对象的成员变量的值完全相同 instanceof 判断对象类型 //a是否为Child对象类型 boolean b = a ...
- win8 hyper-v 禁用不必卸载虚拟机
转载:http://tylzwp.blogbus.com/logs/232938121.html 禁用hyperv的目的是使用之前在用的VMware的虚拟机,不必重新处理一遍. 具体操作: 1确报之前 ...
- 工具----IcoFX
IcoFX IcoFX 是一款免费的图标编辑工具,让您轻松创建 Windows XP 和 Windows Vista 图标. 在编辑区您可以轻松的预览.保存.更改您的图标.您可以将您喜欢的图像转换为图 ...