这道题是网络原题,题目地址:http://www.codeforces.com/contest/85/problem/D
读完题目你可以很快知道这是一道线段树的题目,题目意思是:3种操作。插入一个数,删除一个数,都保证序列有序。以及求和,其中求和是将下标%5==3的所有数求和。
思路是:用5棵线段树维护,s[0]表示%5==1的下标,其他依次类推 cnt,记录子树的元素个数。
想要得到该区间内所有模5等3所有元素的和,左孩子可以求到,两个孩子相互独立,所以求右孩子需要知道(左子树含有 )多少个元素,因为这样分开求的时候,我们才知道 求右孩子时应该求下标模5等几()的所有元素的和。
假如我们 求 i == 3 时(表示 %5 == 4),对于左子树我们直接求就可以 ,但是对于右子树,我们要知道在 i== 3 的情况下 ,右子树的 元素的下标应该是 %5 ==几?所以就是(当前位置减去左子树的元素个数)%5
ans[rt][i]=ans[ls][i]+ans[rs][(i-sum[ls]%5+5)%5];这样敲代码就是一件比较轻松的事情了。
说明:
unique(num,mun+n)返回的是num去重后的尾地址,并不是真正把重复的元素删除,而
是,该函数把重复的元素移到后面去了,依然保存到了原数组中,函数返回去重后最
后一个元素的地址,因为unique去除的是相邻的重复元素,所以一般用之前都会要排
一下序。
lower_bound(num,num+n,x)返回的是num前n个数中出现的第一个“>=x”的数的位置。
注意:这两个函数用之前都要排序。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define mid (l+r)>>1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define MAXN 100010
int sum[MAXN<<2]; //区间的元素个数
__int64 ans[MAXN<<2][5];//分别表示mod5
int key[MAXN];//离散化用
int x[MAXN];
char op[MAXN][10];
void pushup(int rt)
{
for(int i=0;i<5;i++)
{
ans[rt][i]=ans[ls][i]+ans[rs][(i-sum[ls]%5+5)%5];
}
}
void build(int l,int r,int rt)
{
sum[rt]=0;
memset(ans[rt],0,sizeof(ans[rt]));
if(l==r)
return;
int m = mid;
build(lson);
build(rson);
}
int flag;//判断用
void update(int p,int l,int r,int rt)
{
sum[rt]+=2*flag-1;
if (l==r)
{
ans[rt][1]=flag*key[p];//叶子节点的值mod5肯定为1
return ;
}
int m=mid;
if (p<= m)
update(p,lson);
else
update(p,rson);
pushup(rt);
}
int main()
{
int n,tot=0;
while(scanf("%d",&n) != EOF)
{
tot=0;
for(int i=0;i<n;i++)
{
scanf("%s",op[i]);
if(op[i][0]=='a' || op[i][0]=='d')
{
scanf("%d",&x[i]);
key[tot++]=x[i];
}
}
sort(key,key+tot);
tot = unique(key,key+tot) - key;
build(1,tot,1);
for(int i=0;i<n;i++)
{
int pos = lower_bound(key,key+tot,x[i]) - key;
if(op[i][0] == 's')
printf("%I64d\n",ans[1][3]);
else
{
if(op[i][0]=='a')
flag=1;
else
flag=0;
update(pos,1,tot,1);
}
}
}
return 0;
}