求余区间的求和类问题 离线+线段树 HDU4228

题目大意:给一个数组a,他的顺序是严格的单调增,然后有如下三个操作

①加入一个val到a数组里面去,加入的位置就是a[i-1]<val<a[i+1]

②删除一个a[i]=val的值

③查询所有下标i%5=3的值

思路:线段树+离线

首先因为线段树中不支持添加、删除操作的,所以只能离线把所有的val离散化以后放到区间里面去。然后关键就是线段树是怎么建立的。

我们知道,每个%5都会有0,1,2,3,4这5个值,然后我们可以通过线段树来维护这5个值。我们首先用sum[5]表示能被5整除的5种求余后不同类型的数的val,然后再用cnt记录当前这个区间里面还存在的数值。接下来我们定义父亲区间,左子区间和右子区间,然后我们发现左子区间的区间范围和父亲区间的是一样的,然后右子区间的范围要发生改变,他的位置改变到如下位置:(i+cnt)%5,其中i表示右子区间的第几个区间,然后cnt表示左子区间的有效的个数。然后我们就这样去维护就好啦。

//看看会不会爆int!数组会不会少了一维!
//取物问题一定要小心先手胜利的条件
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define ALL(a) a.begin(), a.end()
#define pb push_back
#define mk make_pair
#define fi first
#define se second
const int maxn = 1e5 + 5;
struct point{
    LL sum[5];
    int cnt;
    void init(){
        this -> cnt = 0;
        memset(sum, 0, sizeof(sum));
    }
}tree[maxn << 2];
LL a[maxn];
int q;
pair<char, LL> ch[maxn];

void buildtree(int o, int l, int r){
    if (l == r){
        tree[o].init();
        return ;
    }
    int mid = (l + r) / 2;
    if (l <= mid) buildtree(o << 1, l, mid);
    if (r > mid) buildtree(o << 1 | 1, mid + 1, r);
    tree[o].init();
}

inline void push_up(int o){
    tree[o].cnt = tree[o << 1].cnt + tree[o << 1 | 1].cnt;
}

void display(int o, int l, int r){
    printf("o = %d l = %d r = %d
", o, l, r);
    for (int i = 0; i < 5; i++) printf("%d ", tree[o].sum[i]);
    printf("
");
}

void update(int o, int l, int r, int pos, bool flag){
    if (l == r && l == pos){
        if (flag) {tree[o].sum[1] += a[pos]; tree[o].cnt = 1;}
        else {tree[o].sum[1] -= a[pos]; tree[o].cnt = 0;}
        return ;
    }
    int mid = (l + r) / 2;
    if (pos <= mid) update(o << 1, l, mid, pos, flag);
    if (pos > mid) update(o << 1 | 1, mid + 1, r, pos, flag);
    memset(tree[o].sum, 0, sizeof(tree[o].sum));
    for (int i = 0; i < 5; i++){
        int j = (i + tree[o << 1].cnt) % 5;
        tree[o].sum[i] += tree[o << 1].sum[i];
        tree[o].sum[j] += tree[o << 1 | 1].sum[i];
    }
    ///display(o, l, r);
    push_up(o);
    return ;
}

int main(){
    while (scanf("%d", &q) == 1 && q){
        int n = 0;
        for (int i = 1; i <= q; i++){
            char s[4]; LL tmp = -1;
            scanf("%s", s);
            if (s[0] == 'd' || s[0] == 'a') scanf("%I64d", &tmp);
            ch[i] = mk(s[0], tmp);
            if (s[0] == 'a') a[++n] = tmp;
        }
        sort(a + 1, a + 1 + n);///有待商榷
        buildtree(1, 1, n);
        for (int i = 1; i <= q; i++){
            pair<char, LL> p = ch[i];
            if (p.first == 's'){
                printf("%I64d
", tree[1].sum[3]);
                continue;
            }
            else {
                int pos = lower_bound(a + 1, a + 1 + n, p.second) - a;
                update(1, 1, n, pos, p.first == 'a');
            }
        }
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/heimao5027/p/5869807.html