题目来源:https://codeforces.com/problemset/problem/85/D
★这题看输入感觉是线段树,但是写的时候没有一点思路。。。
题意:
有3种操作
1.添加一个数到集合中
2.删除集合中的某个元素
3.输出 集合排序后 序号mod5==3 的所有元素的和
其中:不会往集合中添加 已经出现过的元素,也不会无缘无故删去 集合中没有的元素
思路:
本题解是通过 离线处理 的(即先保存所有步骤,再逐一处理)
保存步骤的过程中,记录所有添加过的数,并进行 离散化处理 ,以此建立线段树
每个子节点对应的是一个区间,很明显这还不够。我们要给 每一个区间存储5个值,因为模数是5
用数组sum[ maxn<<2 ][ 5 ]记录,sum[ k ][ i ] 表示区间 k 的 序号mod5==i 的所有元素的和
通过 sum[k][i]=sum[k<<1][i]+sum[k<<1|1][(i-cnt[k<<1]%5+5)%5];
来更新 就可求得结果
代码:
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<deque>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=1e5+5;
const int sz=1<<6;
const int inf=2e9;
const int mod=1e9+7;
typedef long long LL;
int n,m;
int a[maxn],b[maxn],op[maxn];
LL cnt[maxn<<2],sum[maxn<<2][5];
template<class T>
inline void read(T &x)
{
char c;x=1;
while((c=getchar())<'0'||c>'9') if(c=='-') x=-1;
T res=c-'0';
while((c=getchar())>='0'&&c<='9') res=res*10+c-'0';
x*=res;
}
void update(int k,int l,int r,int pos,int val,int opt)
{
if(l==r){
sum[k][0]+=val;
cnt[k]+=opt;
return ;
}
int mid=l+r>>1;
if(mid>=pos) update(k<<1,l,mid,pos,val,opt);
else update(k<<1|1,mid+1,r,pos,val,opt);
cnt[k]=cnt[k<<1]+cnt[k<<1|1];
for(int i=0;i<=4;i++){
sum[k][i]=sum[k<<1][i]+sum[k<<1|1][(i-cnt[k<<1]%5+5)%5];
}
}
int main()
{
read(n);
char s[10];
m=0;
for(int i=1;i<=n;i++){
scanf("%s",s);
if(s[0]=='a'){
read(b[i]);
op[i]=1;
a[++m]=b[i];
}
else if(s[0]=='d'){
read(b[i]);
op[i]=-1;
}
else op[i]=0;
}
sort(a+1,a+m+1);
m=unique(a+1,a+m+1)-a-1;
for(int i=1;i<=n;i++){
if(op[i]){
int p=lower_bound(a+1,a+m+1,b[i])-a;
update(1,1,m,p,b[i]*op[i],op[i]);
}
else{
cout<<sum[1][2]<<endl;
}
}
return 0;
}