題目鏈接: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;
}