题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4288
题目大意:
维护一个有序数列{An},有三种操作:
1:add x 就是把x插进去
2:delete x 就是把x删除
3:sum 就是求下标%5=3的元素的和。
题目要求插入和删除后都要保证数列有序大佬的分析:http://blog.csdn.net/dgq8211/article/details/7999179
由于线段树中不支持添加、删除操作,所以题解写的是用离线做法。
我们来看它是如何解决添加、删除的问题的。
首先将所有出现过的数记录下来,然后排序去重,最后根据去重结果建树,然后每个操作数都会对应线段树中的一个点。
遇到添加、删除操作的时候,只要把那个节点的值改变,然后将它对下标的影响处理好就可以。
那么如何处理这些操作对下标的影响呢?
现在我们考虑一个父区间,假设它的左右子区间已经更新完毕。
显然,左区间中下标%5的情况与父区间中下标%5的情况完全相同;
可是,右区间中却不一定相同,因为右区间中的下标是以 mid 当作 1 开始的。
那么,只要我们知道左区间中有效元素的个数 cnt,我们就能知道右区间中的下标 i 在父区间中对应的下标为 i+cnt。
所以,虽然我们最终要的只是总区间中下标%5 = 3的和。但是在更新时我们需要知道右区间%5的所有情况。
于是我们要在线段树的每个节点开一个 sum[5] 和一个 cnt,分别记录这个节点中下标%5的5种情况的和与有效元素的个数。
而查询时,直接访问总区间的 sum[3] 即可。
如此,题目便可解了。复杂度O(M logN logN)。
我的一句话题解:
先读入所有的数据,离散化后建立线段树。在每个结构体中设立一个cnt变量用于维护这段区间内数的个数。这道题的难点在于%5=3这个条件。通过左子区间内元素的个数,就可以知道,在右子区间内,元素的相对位置。
收获:
主要在于对取余确定位置这一技巧的掌握。这是一个非常有用的知识点!
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn = 100005;
char str[maxn][10];
int num[maxn], s[maxn];
int len, flag;
struct Node
{
int l, r, cnt;
ll sum[5];//sum[i]保存%5的各组和
}segTree[maxn<<2];
int ser(int k)
{
int l = 0, r = len - 1, mid;
while(l <= r)
{
mid = (l+r) >> 1;
if(s[mid] < k)
l = mid + 1;
else if(s[mid] > k)
r = mid - 1;
else
return mid;
}
}
void build(int l, int r, int rt)
{
segTree[rt].l = l;
segTree[rt].r = r;
segTree[rt].cnt = 0;
memset(segTree[rt].sum, 0, sizeof(segTree[rt].sum));
if(l == r)
return;
int mid = (l + r) >> 1;
build(l, mid, rt<<1);
build(mid+1, r, rt<<1|1);
}
void pushup(int rt)
{
for(int i = 0; i < 5; i++)
segTree[rt].sum[i] = segTree[rt<<1].sum[i] + segTree[rt<<1|1].sum[((i-segTree[rt<<1].cnt)%5 + 5) % 5];
//这个区间的和是左右子树同样余数的相加和,右子树要用序号减去左子树的个数对5取模后为了保持正数所以要+5再取模,所以是(i-segTree[x*2].cnt)%5+5
}
void update(int rt, int pos, int val)
{
segTree[rt].cnt += 2*flag - 1;//区间内有几个数字
if(segTree[rt].l == segTree[rt].r)
{
segTree[rt].sum[0] = flag * val;//删除则此位清0,add则进行更新
return ;
}
int mid = (segTree[rt].l + segTree[rt].r) >> 1;
if(pos <= mid)
update(rt<<1, pos, val);
else
update(rt<<1|1, pos, val);
pushup(rt);
}
int main()
{
int n, pos;
while(scanf("%d", &n) != EOF)
{
len = 0;
for(int i = 0; i < n; i++)
{
scanf("%s", str[i]);
if(str[i][0] != 's')
{
scanf("%d", &num[i]);
s[len++] = num[i];
}
}
sort(s, s+len);
len = unique(s, s+len) - s;//去重
if(!len)
memset(segTree[1].sum, 0, sizeof(segTree[1].sum));
else
build(1, len, 1);
for(int i = 0; i < n; i++)
{
if(str[i][0] == 'a')
{
flag = 1;
pos = ser(num[i]);
update(1, pos, num[i]);
}
else if(str[i][0] == 'd')
{
flag = 0;
pos = ser(num[i]);
update(1, pos, num[i]);
}
else
printf("%I64d\n", segTree[1].sum[2]);
}
}
return 0;
}