ACM:平衡树(3)——另一个Splay
2016-07-14 12:29
351 查看
题目来源:
HihoCoder1333
题目描述:
定义一个包含数字的集合,集合的初始情况为空。给定下面两种操作:
插入:向集合中添加一个数字k。
询问:询问区间[a, b]内元素的总和。
更新:更新落在区间[a, b]内的数字,将其所有数字都增加k。
删除:删除落在区间[a, b]内的所有数字。
题目要求对于每次询问,输出对应的答案。
解答:
相比HihoCoder1329,本题又增加了更新数据的操作。本题也可以通过构建Splay树来完成,更新操作和删除操作类似,可以通过splay操作将区间内的所有元素都转移到一棵子树中,然后将其元素更新即可。这样的策略的执行效率和树的节点数目有关,当树的节点数目很多时,效率不是很高,我们需要对节点的结构进行改进,提高更新操作的效率。
·数据结构:
本题的询问、更新、删除操作均涉及到了区间。并且更新操作使批量更新。因此,我们设计树的节点的结构如下:
其中: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
程序代码:
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; }
相关文章推荐
- 搜狗百度360市值齐跌:搜索引擎们陷入集体焦虑?
- 本人即将筹备败家日志,敬请期待!
- IE:使用搜索助手
- C++深度优先搜索的实现方法
- 基于文本的搜索
- php实现搜索一维数组元素并删除二维数组对应元素的方法
- 使用Sphinx对索引进行搜索
- asp 多关键词搜索的简单实现方法
- C#使用foreach语句搜索数组元素的方法
- WordPress中用于获取搜索表单的PHP函数使用解析
- JavaScript中数组的排序、乱序和搜索实现代码
- jquery ztree实现树的搜索功能
- 【经典源码收藏】jQuery实用代码片段(筛选,搜索,样式,清除默认值,多选等)
- C#编程实现Excel文档中搜索文本内容的方法及思路
- sqlserver中在指定数据库的所有表的所有列中搜索给定的值
- 可以用来搜索当前页面内容的js代码
- 全文搜索和替换
- javascript搜索自动提示功能的实现第1/3页
- iOS应用中UISearchDisplayController搜索效果的用法
- mysql 模糊搜索的方法介绍