hdu 4288 Coder (线段树)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4288


题意:对一个集合,更确切地说是数组吧,第步有三种操作:1、添加一个数x,2、删除一个数x,3、询问数组中下标index对5取余为3的所有无素的和。

添加一个数时,保证集合中不含此数,同样删除时保证含有此数。


解析:这道题目需要维护5颗线段树,sum[ 5 ]分别表示区间内下标模5为i的所有元素的和,cnt[ ]是统计该区间内现存操作数的个数,先预读所有操作,统计一下数据,可以确定建树的规模。


如果还不是很清楚维护该区间内模5所有情况的和的话,这里再说一下,想要得到该区间内所有模5等3所有元素的和,左孩子可以求到,两个孩子相互独立,所以求右孩子需要知道含有多少个元素,因为这样分开求的时候,我们才知道求右孩子时应该求下标模5等几(==index)的所有元素的和,index在整个数组中下标模5等3,好了,说的真够咬嘴。。。就是这个意思了


参考代码:

#include
#include
#include
#include
#include
#include
using namespace std;

typedef __int64 LL;
const int N = 100050;

struct segment_tree{
	int cnt,lson,rson;
	LL sum[5];
}T[N*4];
int pos,k;

int build(int l,int r)
{
	if(l == r) return pos++;
	int mid = (l+r) / 2,p = pos++;
	T[p].lson = build(l,mid);
	T[p].rson = build(mid+1,r);
	return p;
}

char str[N];
int num[N],op[N],total;

void update(int l,int r,int p,int root)
{
	T[root].cnt += 2 * k - 1;  // 添加或删除元素个数
	if(l == r)
	{
		T[root].sum[0] = num[p]*k; // 添加或删除元素
		return ;
	}
	int mid = (l+r)/2;
	if(p <= mid) update(l,mid,p,T[root].lson);
	else update(mid+1,r,p,T[root].rson);
	for(int i = 0;i < 5;++i)  // 更新该区间内模5所有情况的和
		T[root].sum[i] = T[ T[root].lson ].sum[i] + T[ T[root].rson ].sum[((i-T[ T[root].lson ].cnt)%5+5)%5 ];
}

int main()
{
	int n;
	while(scanf("%d",&n) != EOF)
	{
		memset(T,0,sizeof(T));
		pos = 0;
		total = 0;
		for(int i = 0;i < n;++i)
		{
			scanf("%s",str+i);
			if(str[i] != 's')
			{
				scanf("%d",&num[total]);
				op[i] = num[total++];
			}
		}
		sort(num,num+total);
		int max_n = unique(num,num+total) - num; // 去重并统计操作数个数
		build(0,max_n);
		for(int i = 0;i < n;++i)
		{
			if(str[i] == 's') 
			{
				printf("%I64d\n",T[0].sum[2]);
				continue;
			}
			int p = lower_bound(num,num+max_n,op[i]) - num;  // 二分查找该操作数的位置,以确定该操作数在线段树中的位置
			if(str[i] == 'a') k = 1,update(0,max_n,p,0);  // k的值是在实现添加或删除元素时的巧妙使用
			else if(str[i] == 'd') k = 0,update(0,max_n,p,0);
		}
	}
	return 0;
}


你可能感兴趣的:(数据结构)