题目链接:点击打开链接
题意:给定一个序列,有三种操作:1.add一个数,把这个数插入到序列中使序列维持递增的位置,2.del一个数,删除这个数,3.sum 查询当前序列中所有位置%5等于3的数的和。
线段树,离线查询+离散化
由于数的范围是10^9,但是只有10^5个数据所以可以离散化。而这就需要统计全部输入中总共出现了多少个数,所以要先将所有命令读入,在以出现过的数的数量建树,离线查询。
线段树每个结点维持5个值,分别存储当前区间中%5=1,2,3,4,5的数的和,这样区间合并方程就是:
void pushup(int rt){
p[rt].cnt=p[lson].cnt+p[rson].cnt;
for(int i=0;i<5;i++){
p[rt].sum[i]=p[lson].sum[i]+p[rson].sum[((i+5-p[lson].cnt)%5+5)%5];
}
}
离散化用到的两个函数:
unique(a+1,a+N+1)-(a+1)返回a数组中不相等的元素个数
lower_bound(a+1,a+N+1,x) -(a+1)查询a中第一个大于等于x的数的位置下标(从0开始),由于我建树是从1开始,查出的位置要加1
要注意输入数为0的情况,建树时要改成1
代码:
#include <iostream> #include <algorithm> #include <cstdio> using namespace std; #define lson rt<<1 #define rson rt<<1|1 #define MAX 100010 #define ll long long struct node{ int l,r; ll sum[5]; int cnt; }p[MAX<<2]; char oper[MAX][5]; ll a[MAX]; ll b[MAX]; void pushup(int rt){ p[rt].cnt=p[lson].cnt+p[rson].cnt; for(int i=0;i<5;i++){ p[rt].sum[i]=p[lson].sum[i]+p[rson].sum[((i+5-p[lson].cnt)%5+5)%5]; } } void build(int l,int r,int rt){ p[rt].l=l; p[rt].r=r; p[rt].cnt=0; for(int i=0;i<5;i++) p[rt].sum[i]=0; if(l==r) return ; int m=(l+r)>>1; build(l,m,lson); build(m+1,r,rson); } void update(int pos,ll v,int rt,int c){ if(p[rt].l==p[rt].r){ p[rt].cnt+=c; p[rt].sum[0]+=v; return ; } int m=(p[rt].l+p[rt].r)>>1; if(m>=pos)update(pos,v,lson,c); else update(pos,v,rson,c); pushup(rt); } int main(){ int N; while(~scanf("%d",&N)){ int num=0; for(int i=1;i<=N;i++){ scanf("%s",oper[i]); if(oper[i][0]!='s'){ scanf("%I64d",&a[i]); b[++num]=a[i]; } } sort(b+1,b+num+1); num=unique(b+1,b+num+1)-(b+1); if(num==0)num=1; build(1,num,1); for(int i=1;i<=N;i++){ if(oper[i][0]=='s'){ printf("%I64d\n",p[1].sum[2]); continue; } else{ int pos=lower_bound(b+1,b+num+1,a[i])-(b+1); if(oper[i][0]=='a') update(pos+1,a[i],1,1); else update(pos+1,-a[i],1,-1); } } } return 0; }