poj_3468 伸展树
题目大意
一个数列,每次操作可以是将某区间数字都加上一个相同的整数,也可以是询问一个区间中所有数字的和。(这里区间指的是数列中连续的若干个数)对每次询问给出结果。
思路
1. 伸展树的一般规律
对于区间的查找更新操作,可以考虑使用伸展树、线段树等数据结构。这里使用伸展树来解决。
伸展树对数组进行维护的核心思想是,将需要维护的一组数单独提取出来,形成一棵子树(一般为整棵树的根节点的右子节点的左孩子节点 为根),然后再这个子树上进行操作。此时进行某些操作(如 ADD, SUM 等),只需要在根节点上做个标记,进行延迟处理(即在之后真正访问子节点时候才对子节点进行实际的更新操作),这样可以节省时间。
每次对树的节点进行修改(比如DELETE, INSERT等)之后,都要进行维护信息,此时需要Update一下,然后将该节点旋转至树根。
且在寻找一个区间的起始点对应在树中的节点的时候,都要将该节点所需要的所有信息带给该节点,这就要求在从根节点向下寻找该节点的时候,将路径上的所有节点(即该节点的祖先节点)上的标记都往下传,即PushDown。
2. 针对本题的分析
此题中的节点信息需要维护 data(节点的数据大小)、size(以该节点为根的子树的大小)、lazy(节点需要加的数值)、sum(以该节点为根的子树所代表的区间在此刻的区间和)
在每次找到一个区间,形成一棵子树,对该区间进行加一个数值D的时候,需要在该子树根节点位置的lazy加上值D而不需要下放到每个树中的子节点,同时该节点的sum值需要加上 D*节点的size。
在节点下放的PushDown过程中,进行如下操作:
//向下更新信息
void PushDown(int node){
int left = gTreeNode[node].child[];
int right = gTreeNode[node].child[]; long long tmp_add = gTreeNode[node].lazy;
if (tmp_add){
gTreeNode[node].data += tmp_add; gTreeNode[left].lazy += tmp_add;
gTreeNode[left].sum += (gTreeNode[left].size * tmp_add); gTreeNode[right].lazy += tmp_add;
gTreeNode[right].sum += (gTreeNode[right].size * tmp_add);
gTreeNode[node].lazy = ;
}
}
在节点的Update过程中,需要进行如下操作:
//维护本节点信息
void Update(int node){
gTreeNode[node].sum = gTreeNode[node].data;
gTreeNode[node].size = ;
int left = gTreeNode[node].child[], right = gTreeNode[node].child[];
if (left){
gTreeNode[node].size += gTreeNode[left].size;
gTreeNode[node].sum += gTreeNode[left].sum;
}
if (right){
gTreeNode[node].size += gTreeNode[right].size;
gTreeNode[node].sum += gTreeNode[right].sum;
}
//注意,此时的sum值属于以该节点为根的子树,因此每次都需要从 gTreeNode[node].data 开始加起,而不应该保存原来的值。(这个好像不直观,但画图是可以证明在Splay中,这个sum值是可以保证始终为以该节点为根的子树区间和的)
}
以及需要注意数据的范围, data、lazy和sum均需要为 64位整数。
实现(c++)
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#define MIN(a,b) a<b? a:b
#define MAX_NODE_NUM 100005
#define INFINITE 1 << 30 struct TreeNode{
long long data;
int parent;
int child[];
int child_dir; //程序相关的信息
long long sum;
long long lazy;
int size;
//节点的索引为0,表示该节点为无效节点。若节点的parent = 0, 表示该节点为根节点,若节点的子节点为0,表示没有相应的子节点
TreeNode(int d = INFINITE) :
data(d), parent(), child_dir(), sum(), lazy(), size(){
child[] = child[] = ;
}
void Reset(){
parent = ;
child[] = child[] = ;
size = ;
lazy = ;
sum = ;
}
}; TreeNode gTreeNode[MAX_NODE_NUM];
int gNumber[MAX_NODE_NUM];
int gNodeCount;
int gRootIndex; void LinkNode(int par, int ch, int dir){
gTreeNode[par].child[dir] = ch;
gTreeNode[ch].parent = par;
gTreeNode[ch].child_dir = dir;
}
//维护本节点信息
void Update(int node){
gTreeNode[node].sum = gTreeNode[node].data;
gTreeNode[node].size = ;
int left = gTreeNode[node].child[], right = gTreeNode[node].child[];
if (left){
gTreeNode[node].size += gTreeNode[left].size;
gTreeNode[node].sum += gTreeNode[left].sum;
}
if (right){
gTreeNode[node].size += gTreeNode[right].size;
gTreeNode[node].sum += gTreeNode[right].sum;
}
} //向下更新信息
void PushDown(int node){
int left = gTreeNode[node].child[];
int right = gTreeNode[node].child[]; long long tmp_add = gTreeNode[node].lazy;
if (tmp_add){
gTreeNode[node].data += tmp_add; gTreeNode[left].lazy += tmp_add;
gTreeNode[left].sum += (gTreeNode[left].size * tmp_add); gTreeNode[right].lazy += tmp_add;
gTreeNode[right].sum += (gTreeNode[right].size * tmp_add);
gTreeNode[node].lazy = ;
}
} int BuildTree(int beg, int end){
if (beg > end){
return ;
}
if (beg == end){
gTreeNode[gNodeCount].data = gTreeNode[gNodeCount].sum = gNumber[beg];
return gNodeCount++;
}
int mid = (beg + end) / ;
int left = BuildTree(beg, mid - );
int right = BuildTree(mid + , end); gTreeNode[gNodeCount].data = gTreeNode[gNodeCount].sum = gNumber[mid];
LinkNode(gNodeCount, left, );
LinkNode(gNodeCount, right, );
Update(gNodeCount);
return gNodeCount++;
} //zig or zag旋转
void Rotate(int x){
if (x == gRootIndex){
return;
}
int y = gTreeNode[x].parent;
PushDown(y);
PushDown(x);
int d = gTreeNode[x].child_dir; int z = gTreeNode[y].parent;
LinkNode(z, x, gTreeNode[y].child_dir);
LinkNode(y, gTreeNode[x].child[!d], d);
LinkNode(x, y, !d);
Update(y);
if (y == gRootIndex){
gRootIndex = x;
}
} //旋转操作,将node节点旋转到 f 节点下方
void Splay(int x, int f){
if (x == f){
return;
}
PushDown(x);
int y = gTreeNode[x].parent, z = ;
while (y != f){
z = gTreeNode[y].parent;
if (z == f){
Rotate(x);
break;
}
if (gTreeNode[x].child_dir == gTreeNode[y].child_dir){ //一字型旋转
Rotate(y);
Rotate(x);
}
else{ //之字形旋转
Rotate(x);
Rotate(x);
}
y = gTreeNode[x].parent;
}
Update(x);
}
//获取伸展树中 第k个节点的index
int GetKthInTree(int k){
int node = gRootIndex, left, tmp_size;
while (node){
PushDown(node); //注意要将与该节点有关的信息带下去
left = gTreeNode[node].child[];
tmp_size = gTreeNode[left].size;
if (!left){//left 为空节点
if (k == ){
return node;
}
else{
node = gTreeNode[node].child[];
k--;
continue;
}
}
if (tmp_size + == k){
return node;
}
else if (tmp_size >= k){
node = left;
}
else{
node = gTreeNode[node].child[];
k -= (tmp_size + );
}
}
return -;
} //选择区间,返回由该区间构成的子树的节点。节点为 根节点的右子节点的左子节点
int SelectInterval(int x, int y){
if (x <= || y > gNodeCount){
printf("fuck this splay tree!!!\n");
return -;
}
if (x == && y == gNodeCount - ){
return gRootIndex;
}
int node;
if (x == ){
node = GetKthInTree(y + );
Splay(node, );
return gTreeNode[node].child[];
}
if (y == gNodeCount - ){
node = GetKthInTree(x - );
Splay(node, );
return gTreeNode[node].child[];
}
int node_beg = GetKthInTree(x - );
Splay(node_beg, );
int node_end = GetKthInTree(y + );
Splay(node_end, gRootIndex); return gTreeNode[node_end].child[];
}
void Add(int x, int y, int d){
int node = SelectInterval(x, y);
gTreeNode[node].lazy += d;
gTreeNode[node].sum += gTreeNode[node].size * d;
Splay(node, );
} long long GetSum(int x, int y){
int node = SelectInterval(x, y);
//获得节点之后,一定要进行更新!!!
PushDown(node);
Update(node);
return gTreeNode[node].sum;
} void debug(int node){
if (node){
debug(gTreeNode[node].child[]); printf("node %d, parent = %d, left = %d, right = %d, data = %d, sum = %d, lazy = %d\n",
node, gTreeNode[node].parent, gTreeNode[node].child[], gTreeNode[node].child[],
gTreeNode[node].data, gTreeNode[node].sum, gTreeNode[node].lazy); debug(gTreeNode[node].child[]);
}
}
int main(){
int node_num, query_num;
gNodeCount = ;
scanf("%d%d", &node_num, &query_num);
for (int i = ; i < node_num; i++){
scanf("%d", gNumber + i);
} gRootIndex = BuildTree(, node_num - ); //递归的方式构造一棵开始就平衡的二叉树
gTreeNode[gRootIndex].parent = ; //根 char op[];
int x, y, tmp; for (int i = ; i < query_num; i++){
// debug(gRootIndex); scanf("%s", op);
if (op[] == 'C'){
scanf("%d%d%d", &x, &y, &tmp);
Add(x, y, tmp);
}
else if (op[] == 'Q'){
scanf("%d%d", &x, &y);
printf("%lld\n", GetSum(x, y));
} }
return ;
}
poj_3468 伸展树的更多相关文章
- Splay伸展树学习笔记
Splay伸展树 有篇Splay入门必看文章 —— CSDN链接 经典引文 空间效率:O(n) 时间效率:O(log n)插入.查找.删除 创造者:Daniel Sleator 和 Robert Ta ...
- 纸上谈兵:伸展树(splay tree)
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 我们讨论过,树的搜索效率与树的深度有关.二叉搜索树的深度可能为n,这种情况下,每次 ...
- SplayTree伸展树的非递归实现(自底向上)
Splay Tree 是二叉查找树的一种,它与平衡二叉树.红黑树不同的是,Splay Tree从不强制地保持自身的平衡,每当查找到某个节点n的时候,在返回节点n的同时,Splay Tree会将节点n旋 ...
- 伸展树(一)之 图文解析 和 C语言的实现
概要 本章介绍伸展树.它和"二叉查找树"和"AVL树"一样,都是特殊的二叉树.在了解了"二叉查找树"和"AVL树"之后, ...
- 伸展树(二)之 C++的实现
概要 上一章介绍了伸展树的基本概念,并通过C语言实现了伸展树.本章是伸展树的C++实现,后续再给出Java版本.还是那句老话,它们的原理都一样,择其一了解即可. 目录1. 伸展树的介绍2. 伸展树的C ...
- 伸展树(三)之 Java的实现
概要 前面分别通过C和C++实现了伸展树,本章给出伸展树的Java版本.基本算法和原理都与前两章一样.1. 伸展树的介绍2. 伸展树的Java实现(完整源码)3. 伸展树的Java测试程序 转载请注明 ...
- hdu1890 伸展树(区间反转)
对于大神来说这题是水题.我搞这题花了快2天. 伸展树的优点有什么,就是树不管你怎么旋转序列是不会改变得,并且你要使区间反转,只要把第k大的点转到根结点,那么它的左子树就是要交换的区间[l,r),然后交 ...
- POJ 3580 (伸展树)
题目链接: http://poj.org/problem?id=3580 题目大意:对一个序列进行以下六种操作.输出MIN操作的结果. 解题思路: 六个操作,完美诠释了伸展树有多么吊.注意,默认使用L ...
- Splay 伸展树
废话不说,有篇论文可供参考:杨思雨:<伸展树的基本操作与应用> Splay的好处可以快速分裂和合并. ===============================14.07.26更新== ...
随机推荐
- invalid conversion from 'void* (*)()' to 'void* (*)(void*)'
void *thread1() ], NULL, thread1, NULL)) != ) 提示:invalid conversion from 'void* (*)()' to 'void* (*) ...
- mysql慢查询日志开启和存储格式
mysql版本号是mysql5.6.22.安装环境windows7. 1.使用该查询日志能够找到有效率问题的sql语句.并记录下来,进行监控. 能够使用例如以下语句查询和设置慢查询日志 (1) 查看慢 ...
- iOS边练边学--CALayer,非根层隐式动画,钟表练习
一.CALayer UIView之所以能显示在屏幕上,完全是因为他内部的一个图层 在创建UIView对象时,UIView内部会自动创建一个图层(即CALayer对象),通过UIView的layer属性 ...
- Linux下如何查看tomcat是否启动/系统日志等
Linux下如何查看tomcat是否启动/系统日志等 查看tomcat是否启动 ps -ef | grep tomcat 或者 ps -ef | grep java 启动tomcat(在tomca ...
- unicode编码转汉字
String hexB = Integer.toHexString(utfBytes[byteIndex]); //转换为16进制整型字符串 if (hexB.length() <= 2) ...
- 聊聊Javascript中的AOP编程
Duck punch 我们先不谈AOP编程,先从duck punch编程谈起. 如果你去wikipedia中查找duck punch,你查阅到的应该是monkey patch这个词条.根据解释,Mon ...
- Python python的输入输出
#-*- coding:utf-8 -*- #屏蔽中文乱码方案一(官方推荐) #这个语句必须顶行写 #屏蔽中文乱码方案二(不建议使用) '''#coding=utf-8 ''' #input(),输入 ...
- imx6 uart分析
本文主要记录: 1.uart设备注册 2.uart驱动注册 3.上层应用调用有些地方理解的还不是很透彻,希望指正. 1.uart设备注册过程 MACHINE_START(MX6Q_SABRESD, & ...
- R语言中的标准输入,输出, 错误流
在R中,stdin() 对应标准输入流 , stdout() 对应标准输出流,stderr() 对应标准错误流 1) 从标准输入流中读取数据 在R的交互式环境中, R >a <- read ...
- CentOS7.2内核版本查看简述
1.uname 命令 [root@bogon /]# uname --help 用法:uname [选项]... 输出一组系统信息.如果不跟随选项,则视为只附加-s 选项. -a, --all以如 ...