zoukankan      html  css  js  c++  java
  • 求余区间的求和类问题 离线+线段树 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
  • 相关阅读:
    RHEL 6.3安装(超级详细图解教程)[转载]
    CentOS下设置vimrc,添加文件注释信息以及设置tab 键为4 格
    Centos 设置时区和时间以及增加中文输入法
    虚拟机上CentOS6.5 无法上网的解决方法
    LoadRunner 11安装及测试环境搭建
    LR11录制回放出现中文乱码以及录制时一直跳到360浏览器的解决方法
    第 3 章 变量和表达式
    第 2 章 编写 C# 程序
    第 1 章 C# 简介
    jQuery Mobile的学习时间bottonbutton的事件学习
  • 原文地址:https://www.cnblogs.com/heimao5027/p/5869807.html
Copyright © 2011-2022 走看看