Yandex.Algorithm 2011 Round 1 D.Sum of Medians(线段树+离散化)

链接

http://codeforces.com/contest/85

题意

共n个操作,有3种类型

  1. add x
    向集合中添加数x
  2. del x
    将集合中数x删除
  3. sum
    将集合中所有数进行从小到大排序,求出所有数下标对5取余为3的数的和即 ∑ i n a [ i ] ( 其 中 i % 5 = = 3 ) \sum_i^na[i] (其中i\%5==3) ina[i](i%5==3)
分析

这里使用线段树,首先考虑x为1e9,所以对输入的x进行统计,然后离散化,对离散化后的x进行建立一个线段树,显然线段树的每个节点[l,r]需要存储区间[l,r]的下标对5取余为3的数的和,那么必然存在子区间的合并,

这时又有这样一个问题,对于[l1,r1],[l2,r2](l2-2==r1)合并之后区间[l,r]的下标对5取余为3的数的和,来自左子区间[l1,r1]下标对5取余为3的数的和,再加上右子区间的和,但是合并之后是有问题产生的,左子区间下标对5取余为3的数是不变的,对于右子区间来说,对5取余为(0、1、2、3、4)的数都可能成为合并之后下标对5取余为3的数的和,它是与左子区间的个数有关的;假如左子区间个数为8,那么对于右子区间来说,原来它的下标mod5为1的数合并之后,即产生对mod5为3的贡献(模拟一下),所以对于每个节点,我们都需要它的对5取余的所有情况的和(余数为0、1、2、3、4的和),所以需要定义一个mod数组,mod[x]代表余数为x的数的和,并且最关键的是区间的合并;

代码
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <string>
    #include <cstdio>
    #include <vector>
    #include <queue>
    #include <stack>
    #include <cmath>
    #include <set>
    #include <map>
     
    using namespace std;
     
    #define inf 0x7f7f7f7f
    #define maxn 1000005
    #define N 200005
    #define P 2
    typedef long long ll;
     
    int n, k, a[maxn], cnt;
     
    string s[maxn];
    int val[maxn];
     
    struct Tree {
        ll cnt, mod[5];
    } tree[4 * maxn];
     
    void push_up(int x) {
        for (int i = 0; i < 5; i++)
            tree[x].mod[i] = tree[x << 1].mod[i] + tree[x << 1 | 1].mod[((i - tree[x << 1].cnt) % 5 + 5) % 5];
    }
     
    void build(int x, int l, int r) {
        tree[x].cnt = 0;
        if (l == r) {
            memset(tree[x].mod, 0, sizeof(tree[x].mod));
            return;
        }
        int mid = (l + r) >> 1;
        build(x << 1, l, mid);
        build(x << 1 | 1, mid + 1, r);
        push_up(x);
    }
     
    void update(int l, int r, int x, int pos, int val) {
        tree[x].cnt += val;
        if (l == r) {
            if (tree[x].cnt == 0)
                tree[x].mod[1] = 0;
            else tree[x].mod[1] = a[pos];
            return;
        }
        int mid = (l + r) >> 1;
        if (pos <= mid)update(l, mid, x << 1, pos, val);
        else update(mid + 1, r, x << 1 | 1, pos, val);
        push_up(x);
    }
     
    int main() {
        while (scanf("%d", &n) != EOF) {
            for (int i = 1; i <= n; i++) {
                cin >> s[i];
                if (s[i] != "sum") {
                    scanf("%d",&val[i]);
                    a[++cnt] = val[i];
                }
            }
            sort(a + 1, a + 1 + cnt);
            int len = unique(a + 1, a + 1 + cnt) - a - 1;
            if(len == 0)len = 1;
            build(1, 1, len);
            for (int i = 1; i <= n; i++) {
                string ty = s[i];
                if (ty == "add") {
                    int pos = lower_bound(a + 1, a + 1 + len, val[i]) - a;
                    update(1, len, 1, pos, 1);
                } else if (ty == "del") {
                    int pos = lower_bound(a + 1, a + 1 + len, val[i]) - a;
                    update(1, len, 1, pos, -1);
                } else cout << tree[1].mod[3] << endl;
            }
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值