题目链接: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<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<set>
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;
}