题意:
有n个操作,add x表示向集合添加元素x,del x表示删除集合中的x元素,sum表示求集合所有下标i%5==3的元素之和,注意里的元素都是有序且无重复的。
思路:
线段树+离线处理,单点更新,O(1)查询。为每个节点存储两种信息,一种为当树向上更新时右区间需要平移的个数mov,另一种为ans[i]表示该区间内所有下标%5==i的元素的和。然后树的向上更新操作就为
sum[rt].mov=(sum[rt<<1].mov+sum[rt<<1|1].mov)%5;
for(int i=0;i<5;i++){
sum[rt].ans[i]=sum[rt<<1].ans[i]+sum[rt<<1|1].ans[(i-sum[rt<<1].mov+5)%5];
}
接下来是离线处理问题,即先把问题存在数组中。由于这集合的元素都是有序且无重复的。所以我们可以借助set来离散化元素值,使其按从小到大的顺序对应一个位置,最后才能方便地在树上进行加入、删除操作。
#include<cstdio>
#include<set>
#include<map>
#include<cstring>
using namespace std;
typedef __int64 LL;
const int MAX=1e5+5;
struct Node{
int mov;
LL ans[5];
}sum[MAX<<2];
struct Que{
int id,x;
}Q[MAX];
int n;
set<int> s;
map<int,int> pos;
char op[5];
void PushUp(int rt){
sum[rt].mov=(sum[rt<<1].mov+sum[rt<<1|1].mov)%5;
for(int i=0;i<5;i++){
sum[rt].ans[i]=sum[rt<<1].ans[i]+sum[rt<<1|1].ans[(i-sum[rt<<1].mov+5)%5];
}
}
void UpDate(int pos,int c,int l,int r,int rt){
if(l==r){
if(c>0){
sum[rt].mov=1;
sum[rt].ans[1]=c;
}
else{
sum[rt].mov=0;
sum[rt].ans[1]=0;
}
return;
}
int mid=(l+r)>>1;
if(pos<=mid) UpDate(pos,c,l,mid,rt<<1);
else UpDate(pos,c,mid+1,r,rt<<1|1);
PushUp(rt);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",op);
if(op[0]=='a'){
scanf("%d",&Q[i].x);
s.insert(Q[i].x);
Q[i].id=0;
}
if(op[0]=='d'){
scanf("%d",&Q[i].x);
Q[i].id=1;
}
if(op[0]=='s'){
Q[i].id=2;
}
}
int cnt=1;
set<int>::iterator it;
for(it=s.begin();it!=s.end();it++){
pos[(*it)]=cnt++;
}
cnt--;
memset(sum,0,sizeof(sum));
for(int i=1;i<=n;i++){
if(Q[i].id==0){
UpDate(pos[Q[i].x],Q[i].x,1,cnt,1);
}
if(Q[i].id==1){
UpDate(pos[Q[i].x],-Q[i].x,1,cnt,1);
}
if(Q[i].id==2){
printf("%I64d\n",sum[1].ans[3]);
}
}
return 0;
}