您的位置:首页 > 其它

ACM:平衡树(3)——另一个Splay

2016-07-14 12:29 351 查看
题目来源: 

        HihoCoder1333
 
题目描述:
    定义一个包含数字的集合,集合的初始情况为空。给定下面两种操作:
        插入:向集合中添加一个数字k。

        询问:询问区间[a, b]内元素的总和。

        更新:更新落在区间[a, b]内的数字,将其所有数字都增加k。

        删除:删除落在区间[a, b]内的所有数字。

题目要求对于每次询问,输出对应的答案。

解答:
    相比HihoCoder1329,本题又增加了更新数据的操作。本题也可以通过构建Splay树来完成,更新操作和删除操作类似,可以通过splay操作将区间内的所有元素都转移到一棵子树中,然后将其元素更新即可。这样的策略的执行效率和树的节点数目有关,当树的节点数目很多时,效率不是很高,我们需要对节点的结构进行改进,提高更新操作的效率。

·数据结构:
    本题的询问、更新、删除操作均涉及到了区间。并且更新操作使批量更新。因此,我们设计树的节点的结构如下:
   
struct node {key, parent, left, right, sum, count, update, update_value}


其中:key、parent、left、right 分别代表了节点的:关键字、父节点、左右孩子节点,sum记录了以该节点为根的子树内的所有节点值的总和;count则代表了该子树中的节点个数;update、update_value则用于批量更新时的操作,下文中会有说明。 

·插入操作: 
    对于插入操作,本题和HihoCoder1329题类似,这里不多说明。注意新插入的初始化节点sum就是节点值本身,count值为1,update 和 update_value都赋值为0。注意再插入的过程中,新插入节点的所有祖先界定的sum值和count值都要对应更新。

·查询操作:

    查询操作本题也涉及到了区间,可以通过Splay操作将区间内的节点转移到一棵子树中,然后读取树的根节点的sum值即可。

·删除操作:

    删除操作也和1329题相同,通过Splay操作将要删除的节点转移到一棵子树中,然后删除之,注意相关祖先节点的count域和sum域也要做相应改动。

·更新操作:

    更新操作本题中是批量更新,更新给定区间内的所有节点,并统一增加k值。这里首先可以通过splay操作将所有待更新节点转移到一棵子树上。但是这里不会更新子树中所有的节点,而是采用懒惰策略,仅仅将更新信息记录在子树的根节点中。此时更新子树根节点的key值,将子树根节点的update标记置为1,表示该子树数据有更新,并且还尚未同步到其子节点。更新的k值则存储在update_value域中,此时,整个子树中的节点的总和可以直接计算出来,即:sum
= sum + update_value * count;

    
·同步:

    为了保证数据在任何情况下都是正确的,这里需要增加一个新的曹组——同步。同步是指对于一个有更新信息的节点,将其更新信息同步到其左右孩子中,此时设置其左右孩子节点update标记为1,更新左右孩子的key值,更新左右孩子的update_value值。此时就将更新的信息同步到子节点中。最后将当前节点的update值和update_value标记置为0。

    在某些情况下,可能会出现父节点和子节点的update标记同时为1的情况。此时将父节点的更新信息同步到子节点后,要和子节点包含的更新信息整合。方法就是将update_value值叠加即可。

    同步操作保证了树中节点信息的正确性,因此在进行插入、删除、旋转等操作过程中如果遇到update标记为1的节点,都必须先将其更新信息同步后再执行其他的操作。

·虚节点问题:
    本题中同样会遇到虚节点的问题。注意虚节点并不是真实存在的,因此在插入时,节点本身的count域为0,sum的初始值也为0,当通过操作将虚节点转化为非叶节点时,节点的sum域也不能包含该节点的key值。在同步过程中,虚节点的key值也不会更改。

输入输出格式:
    输入:
          
第1行:1个正整数n,表示操作数量,100≤n≤200,000

  第2..n+1行:可能包含下面4种规则:

1个字母'I',紧接着2个数字id,val,表示一个编号为id的新成员加入,其兴趣值为val,保证在团队中的每个人id都不相同。

1个字母'Q',紧接着2个数字a,b。表示询问团队中id在区间[a,b]的所有成员总兴趣值,保证区间内至少有一个成员,结果有可能超过int的范围。

1个字母'M',紧接着3个数字a,b,d,表示将团队中id在区间[a,b]的成员兴趣值都改变d,其中d有可能为负数。保证操作之后每个成员的兴趣值仍然在0~10,000,000。

1个字母'D',紧接着2个数字a,b,表示将团队中id在区间[a,b]的成员除去。

注意有可能出现一个id为1的成员加入团队,被除去之后,又有一个新的id为1的成员加入团队的情况。

 
  输出:
          若干行:每行1个整数,表示针对询问的回答,保证一定有合法的解。

数据范围:

     1≤id≤100,000,000

     1≤val≤10,000,000

程序代码:

/****************************************************/
/* File        : Hiho_Week_105                      */
/* Author      : Zhang Yufei                        */
/* Date        : 2016-07-05                         */
/* Description : HihoCoder ACM program. (submit:g++)*/
/****************************************************/

#include<stdio.h>
#include<stdlib.h>

/* Define structure to store the information of
* the member:
* Parameters:
*		@id: The id of member.
*		@value: The value of interest of this member.
*		@sum: The total value of value in this sub-tree.
*		@count: The number of nodes in this sub-tree.
*		@update: Mark if the value has been updated into child node.
*		@update: Record the update value.
*		@left & @right: The left and right child of node.
*		@parent: The parent node of this node.
*/
typedef struct node {
int id;
int value;
long long sum;
int count;
int update;
int update_value;
struct node *left, *right;
struct node *parent;
} node;

// The root of splay tree.
node *root = NULL;

/*
* This function update the child value in subtree.
* Parameters:
*		@cur: The current root node.
* Returns:
*		None.
*/
void split(node* cur) {
if(cur == NULL || cur->update == 0) {
return;
}

cur->update = 0;
if(cur->left != NULL) {
if(cur->left->value != 0)
cur->left->value += cur->update_value;
cur->left->sum += cur->update_value * cur->left->count;
cur->left->update = 1;
cur->left->update_value += cur->update_value;
}

if(cur->right != NULL) {
if(cur->right->value != 0)
cur->right->value += cur->update_value;
cur->right->sum += cur->update_value * cur->right->count;
cur->right->update = 1;
cur->right->update_value += cur->update_value;
}

cur->update_value = 0;
}

/*
* The left rotate operation.
* Parameters:
*		@cur: The node to rotate.
* Returns:
*		None.
*/
void left_rotate(node* cur) {
node *right = cur->right;
node *right_left = right->left;
node *parent = cur->parent;

split(parent);
split(cur);
split(right);

right->parent = parent;
if(parent != NULL) {
if(cur == parent->left) {
parent->left = right;
} else {
parent->right = right;
}
} else {
root = right;
}

right->left = cur;
cur->parent = right;

cur->right = right_left;
if(right_left != NULL) {
right_left->parent = cur;
}

cur->sum = cur->value;
if(cur->value == 0) {
cur->count = 0;
} else {
cur->count = 1;
}
if(cur->left != NULL) {
cur->sum += cur->left->sum;
cur->count += cur->left->count;
}
if(cur->right != NULL) {
cur->sum += cur->right->sum;
cur->count += cur->right->count;
}

right->sum = right->value;
if(right->value == 0) {
right->count = 0;
} else {
right->count = 1;
}
if(right->left != NULL) {
right->sum += right->left->sum;
right->count += right->left->count;
}
if(right->right != NULL) {
right->sum += right->right->sum;
right->count += right->right->count;
}
}

/*
* The right rotate operation.
* Parameters:
*		@cur: The node to rotate.
* Returns:
*		None.
*/
void right_rotate(node* cur) {
node *left = cur->left;
node *left_right = left->right;
node *parent = cur->parent;

split(parent);
split(cur);
split(left);

left->parent = parent;
if(parent != NULL) {
if(cur == parent->left) {
parent->left = left;
} else {
parent->right = left;
}
} else {
root = left;
}

left->right = cur;
cur->parent = left;

cur->left = left_right;
if(left_right != NULL) {
left_right->parent = cur;
}

cur->sum = cur->value;
if(cur->value == 0) {
cur->count = 0;
} else {
cur->count = 1;
}
if(cur->left != NULL) {
cur->sum += cur->left->sum;
cur->count += cur->left->count;
}
if(cur->right != NULL) {
cur->sum += cur->right->sum;
cur->count += cur->right->count;
}

left->sum = left->value;
if(left->value == 0) {
left->count = 0;
} else {
left->count = 1;
}
if(left->left != NULL) {
left->sum += left->left->sum;
left->count += left->left->count;
}
if(left->right != NULL) {
left->sum += left->right->sum;
left->count += left->right->count;
}
}

/*
* Define the zig operation.
* Parameters:
*		@cur: The current node to operate.
* Returns:
*		None.
*/
void zig(node* cur) {
node* p = cur->parent;
if(p->left == cur) {
right_rotate(p);
} else {
left_rotate(p);
}
}

/*
* Define the zig-zig operation.
* Parameters:
*		@cur: The current node to operate.
* Returns:
*		None.
*/
void zig_zig(node* cur) {
node *p = cur->parent;
node *pp = p->parent;

if(p->left == cur) {
right_rotate(pp);
right_rotate(p);
} else {
left_rotate(pp);
left_rotate(p);
}
}

/*
* Define the zig-zag operation.
* Parameters:
*		@cur: The current node to operate.
* Returns:
*		None.
*/
void zig_zag(node* cur) {
node *p = cur->parent;
node *pp = p->parent;

if(p->left == cur) {
right_rotate(p);
left_rotate(pp);
} else {
left_rotate(p);
right_rotate(pp);
}
}

/*
* Define the splay operation.
* Parameters:
*		@cur: The current node to operate.
*		@dst: The distination position of the operation.
* Returns:
*		None.
*/
void splay(node* cur, node* dst) {
while(cur->parent != dst) {
node* p = cur->parent;
if(p->parent == dst) {
zig(cur);
} else {
node *pp = p->parent;
if(p == pp->left && cur == p->left ||
p == pp->right && cur == p->right) {
zig_zig(cur);
} else {
zig_zag(cur);
}
}
}
}

/*
* This function insert a node into the tree.
* Parameters:
*		@ins: The node to insert.
* Returns:
*		The node to inserted.
*/
node* insert(node* ins) {
node* p = root;
node* pre = NULL;
while(p != NULL) {
pre = p;
split(p);
if(ins->value != 0) {
p->sum += ins->value;
p->count++;
}
if(p->id > ins->id) {
p = p->left;
} else if(p->id < ins->id){
p = p->right;
} else {
if(ins->value != 0) {
p->value = ins->value;
p->sum += p->value;
}
free(ins);
ins = p;
splay(ins, NULL);
return ins;
}
}
if(pre != NULL) {
if(ins->id > pre->id) {
pre->right = ins;
} else {
pre->left = ins;
}
ins->parent = pre;
} else {
root = ins;
}

splay(ins, NULL);

return ins;
}

/*
* This function find the preview node of the current node.
* Parameters:
*		@cur: The current node to search.
* Returns:
*		The preview node.
*/
node* preview(node* cur) {
node* pre = cur->left;
if(pre != NULL) {
while(pre->right != NULL) {
pre = pre->right;
}
} else {
pre = cur;
while(pre != NULL) {
if(pre->parent == NULL) {
pre = NULL;
break;
}
if(pre == pre->parent->right) {
pre = pre->parent;
break;
}
pre = pre->parent;
}
}

return pre;
}

/*
* This function find the next node of the current node.
* Parameters:
*		@cur: The current node to search.
* Returns:
*		The next node.
*/
node* successor(node* cur) {
node* suc = cur->right;
if(suc != NULL) {
while(suc->left != NULL) {
suc = suc->left;
}
} else {
suc = cur;
while(suc != NULL) {
if(suc->parent == NULL) {
suc = NULL;
break;
}
if(suc == suc->parent->left) {
suc = suc->parent;
break;
}
suc = suc->parent;
}
}

return suc;
}

/*
* This function search the nodes according to given range.
* Parameters:
*		@s & @e: The left and right edge of range.
* Returns:
*		The root of the sub-tree according given range.
*/
node* search(int s, int e) {
node* s_node = (node*) malloc(sizeof(node));
node* e_node = (node*) malloc(sizeof(node));

s_node->id = s;
s_node->value = 0;
s_node->sum = 0;
s_node->left = s_node->right = s_node->parent = NULL;
s_node->update = 0;
s_node->update_value = 0;
s_node->count = 0;

e_node->id = e;
e_node->value = 0;
e_node->sum = 0;
e_node->left = e_node->right = e_node->parent = NULL;
e_node->update = 0;
e_node->update_value = 0;
e_node->count = 0;

s_node = insert(s_node);
e_node = insert(e_node);

node* s_pre = preview(s_node);
node* e_next = successor(e_node);

splay(s_pre, NULL);
splay(e_next, s_pre);

return e_next->left;
}

/*
* This function deletes the nodes according to given range.
* Parameters:
*		@s & @e: The left and right edge of range.
* Returns:
*		None.
*/
void remove(int s, int e) {
node *del = search(s, e);
if(del == NULL) {
return;
}

node* p = del->parent;

while(p != NULL) {
p->sum -= del->sum;
p->count -= del->count;
p = p->parent;
}

if(del->parent == NULL) {
root = NULL;
} else if(del->parent->left == del) {
del->parent->left = NULL;
} else {
del->parent->right = NULL;
}
}

/*
* This function update the value of nodes according to given range.
* Parameters:
*		@s & @e: The left and right edge of range.
*		@new_value: The new value to upeate.
* Returns:
*		The root of the sub-tree according given range.
*/
void update_node(int s, int e, int new_value) {
node *up = search(s, e);

if(up != NULL) {
if(up->value != 0) {
up->value += new_value;
}
up->sum += new_value * up->count;
up->update = 1;
up->update_value += new_value;

node *p = up->parent;
while(p != NULL) {
p->sum += new_value * up->count;
p = p->parent;
}
}
}

/*
* The main program.
*/
int main(void) {
int n;
scanf("%d", &n);

node* min = (node*) malloc(sizeof(node));
min->id = 0;
min->value = min->sum = 0;
min->left = min->right = min->parent = NULL;
min->count = 0;
min->update = 0;
min->update_value = 0;

node* max = (node*) malloc(sizeof(node));
max->id = 100000001;
max->value = max->sum = 0;
max->left = max->right = max->parent = NULL;
max->count = 0;
max->update = 0;
max->update_value = 0;

min = insert(min);
max = insert(max);

for(int i = 0; i < n; i++) {
getchar();
char operate;
int a, b, d, id, value;
scanf("%c", &operate);

if(operate == 'I') {
scanf("%d %d", &id, &value);
node *n = (node*) malloc(sizeof(node));
n->id = id;
n->value = value;
n->parent = n->left = n->right = NULL;
n->count = 1;
n->sum = value;
n->update = 0;
n->update_value = 0;
n = insert(n);
} else if(operate == 'Q') {
scanf("%d %d", &a, &b);
a = a > 1 ? a : 1;
b = b < 100000000 ? b : 100000000;
node *r = search(a, b);
printf("%lld\n", r->sum);
} else if(operate == 'M') {
scanf("%d %d %d", &a, &b, &d);
a = a > 1 ? a : 1;
b = b < 100000000 ? b : 100000000;
update_node(a, b, d);
} else if(operate == 'D') {
scanf("%d %d", &a, &b);
a = a > 1 ? a : 1;
b = b < 100000000 ? b : 100000000;
remove(a, b);
}
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  acm 平衡搜索树 搜索