HDU 4288 Coder
题意: 有连续 n 个操作,一开始集合为空,对应有三种操作:
add x : 如果x 不在集合中,加入 x 到集合中
del x : 如果x 在集合中,删除之
sum : 集合中存在的数排好序之后输出满足 序号%5=3 的数值的和
分析: 每个区间维护两个数组
num[i] i 区间内部不同数值的个数
s[i][5] i 区间内部数值序号对 5 取模后各种情况的和
#include<stdio.h> #include<string.h> #include<map> #include<algorithm> using namespace std; #define maxn 100005 #define clr(x)memset(x,0,sizeof(x)) __int64 s[maxn<<2][5]; __int64 val[maxn]; int num[maxn<<2]; map<long long,int>v1; map<long long,int>v2; void creat(int l,int r,int rt) { int i; for(i=0;i<5;i++) s[rt][i]=0; num[rt]=0; if(l==r) { v1[val[l]]=l; // 记录每个排好序的数值所在叶子区间,方便插入和删除 return; } int mid=(l+r)>>1; creat(l,mid,rt<<1); creat(mid+1,r,rt<<1|1); } void add(int x,int l,int r,int rt) { if(l==r) // 找到了区间端点为 x 的位置 { num[rt]=1; s[rt][1]=val[x]; // 只有一个数,所以该区间的第一个数即为插入的数值 return; } int mid=(l+r)>>1; if(x<=mid) add(x,l,mid,rt<<1); else add(x,mid+1,r,rt<<1|1); int i; for(i=0;i<5;i++) // 先累加左子树对5取模各种情况的数值的和到根区间 s[rt][i]=s[rt<<1][i]; int t=num[rt<<1]; // 左子树数值的个数 for(i=0;i<5;i++) s[rt][(i+t)%5]+=s[rt<<1|1][i]; // 累加右子树对5取模各种情况的数值的和到根区间 num[rt]=num[rt<<1]+num[rt<<1|1]; // 累加左子树和右子树在集合中数的个数 } void del(int x,int l,int r,int rt) // 删除操作和插入类似 { if(l==r) { num[rt]=0; s[rt][1]=0; return; } int mid=(l+r)>>1; if(x<=mid) del(x,l,mid,rt<<1); else del(x,mid+1,r,rt<<1|1); int i; for(i=0;i<5;i++) s[rt][i]=s[rt<<1][i]; int t=num[rt<<1]; for(i=0;i<5;i++) s[rt][(i+t)%5]+=s[rt<<1|1][i]; num[rt]=num[rt<<1]+num[rt<<1|1]; } char op[5]; __int64 a[maxn]; int id[maxn]; int main() { int i,n,tot; while(scanf("%d",&n)!=EOF) { tot=0; for(i=1;i<=n;i++) // 离线按顺序保存操作 { scanf("%s",op); if(op[0]=='a') { scanf("%I64d",&a[i]); id[i]=1; val[++tot]=a[i]; // 记录所有可能加入集合的数值 } else if(op[0]=='d') { scanf("%I64d",&a[i]); id[i]=2; } else id[i]=3; } v1.clear(); v2.clear(); sort(val+1,val+1+tot); // 排序好可能加入的数值,离散化 creat(1,tot,1); for(i=1;i<=n;i++) { if(id[i]==1) { if(v2[a[i]]==0) // 如果集合中没有 { v2[a[i]]=1; add(v1[a[i]],1,tot,1); } } else if(id[i]==2) { if(v2[a[i]]==1) // 如果集合中存在 { v2[a[i]]=0; del(v1[a[i]],1,tot,1); } } else printf("%I64d\n",s[1][3]); } } return 0; }