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

本文介绍了一种使用Splay树解决区间查询、更新等问题的方法,包括插入、查询、删除及批量更新操作的具体实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目来源: 
        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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值