segment tree, single-point update
题意
维护一个集合,这个集合可进行以下操作:
+ 向其中添加一个数(保证之前没有这个数)
+ 向其中删除一个数(保证集合中有这个数)
+ 求所有下标%5==3的数的和(从小到大排列)
完成给定的操作,返回sum的值
分析
求区间和问题,尝试使用线段树。然而是求的有步长的和,怎么处理?首先,每个区间中记录下标模5相同的数的和(这个下标是从这个区间的开头开始计数),然后我们考虑线段树中父子结点间的关系:父亲结点中所有下标%5==j的数,一部分也是左子节点中全部%5==j的数,这是显然的;然而对于从右子节点来的那部分,显然因为左子节点中的数,有了一定的偏移,这是我们就要记录偏移量cnt。显然这个偏移量也就是左子节点中的数的个数,所以父亲节点中下标%5==j的数所对应的余数满足关系i=(j+cnt)%5。所以我们可以有不同余数数的和的回溯关系。具体可以见下面代码的pushup部分。
有了这个线段树的基础后,因为x范围太大,所以离线处理,离散化。
AC代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <set>
#include <string>
#include <map>
#include <queue>
#include <deque>
#include <list>
#include <sstream>
#include <stack>
using namespace std;
#define cls(x) memset(x,0,sizeof x)
#define inf(x) memset(x,0x3f,sizeof x)
#define neg(x) memset(x,-1,sizeof x)
#define ninf(x) memset(x,0xc0,sizeof x)
#define st0(x) memset(x,false,sizeof x)
#define st1(x) memset(x,true,sizeof x)
#define lowbit(x) x&(-x)
#define input(x) scanf("%d",&(x))
#define inputt(x,y) scanf("%d %d",&(x),&(y))
#define bug cout<<"here"<<endl;
//#pragma comment(linker, "/STACK:1024000000,1024000000")//stack expansion
//#define debug
const double PI=acos(-1.0);
const int INF=0x3f3f3f3f;//1061109567-2147483647
const long long LINF=0x3f3f3f3f3f3f3f3f;//4557430888798830399-9223372036854775807
const int maxn=100000+100;
int N;
int opr[maxn],num[maxn];
int pos[maxn],p1;
void discrete()
{
sort(pos,pos+p1);
int t=p1;
p1=0;
for(int i=0;i<t;++i)
if(pos[p1]!=pos[i])
pos[++p1]=pos[i];
++p1;
return;
}
/* 线段树 */
struct segNode
{
int left,right;//结点对应的区间端点
/*结点的性质*/
int cnt;
long long sum[5];
};
struct segTree
{
segNode tree[maxn*4+10];
/* 由子结点回溯 */
void Push_Up(int x)
{
tree[x].cnt=tree[x<<1].cnt+tree[x<<1|1].cnt;
for(int i=0;i<5;++i)
{
int j=(i+tree[x<<1].cnt)%5;
tree[x].sum[j]=tree[x<<1].sum[j]+tree[x<<1|1].sum[i];
}
return;
}
/* 向下更新 */
/* 线段树构造函数 */
void build(int x,int left,int right)
{
tree[x].left=left;
tree[x].right=right;
tree[x].cnt=0;
cls(tree[x].sum);
if(left==right)//只有一个元素时
return;
/*递归构造子树*/
int mid=(left+right)>>1;
build(x<<1,left,mid);
build(x<<1|1,mid+1,right);
/* 回溯构造 */
return;
}
/* 单点更新 */
void change(int x,int p,int v)
{
if(tree[x].left==p&&tree[x].right==p)//找到这个点
{
if(v)
{
tree[x].sum[1]=pos[p];
++tree[x].cnt;
}
else
{
tree[x].sum[1]=0;
--tree[x].cnt;
}
return;
}
int mid=(tree[x].left+tree[x].right)>>1;
if(p<=mid)
change(x<<1,p,v);
else
change(x<<1|1,p,v);
/* 回溯更新 */
Push_Up(x);
return;
}
}seq;
int main()
{
//ios::sync_with_stdio(false);
//cin.tie(0);
#ifdef debug
freopen("E:\\Documents\\code\\input.txt","r",stdin);
freopen("E:\\Documents\\code\\output.txt","w",stdout);
#endif
//IO
while(input(N)!=EOF)
{
char op[10];
p1=0;
for(int i=0;i<N;++i)
{
scanf("%s",op);
if(op[0]=='a')
{
opr[i]=1;
input(num[i]);
pos[p1++]=num[i];
}
if(op[0]=='d')
{
opr[i]=0;
input(num[i]);
pos[p1++]=num[i];
}
if(op[0]=='s')
opr[i]=2;
}
discrete();
seq.build(1,0,p1-1);
for(int i=0;i<N;++i)
{
if(opr[i]==2)
printf("%lld\n",seq.tree[1].sum[3]);
else
seq.change(1,lower_bound(pos,pos+p1,num[i])-pos,opr[i]);
}
}
return 0;
}