「一题多解」【CodeForces 85D】Sum of Medians(线段树 / 分块)

4 篇文章 0 订阅
3 篇文章 0 订阅

题目链接

【CodeForces 85D】Sum of Medians


题目大意

实现一个 set s e t ,支持插入,删除,求 a5k+3 ∑ a 5 k + 3 。注意, set s e t 中的数在任何时刻都应该是排好序的。


题解 I

首先想到离线处理,每一个数字有一个离散化后的编号。
线段树的每一个节点维护五个值,分别表示 a5k+t ∑ a 5 k + t ,更新世晚期按照左子树 size s i z e 更新就好了。
时间复杂度: O(nlogn) O ( n log ⁡ n )


代码 I

#include <bits/stdc++.h>
#define ls (p << 1)
#define rs (ls | 1)
#define md ((l + r) >> 1)
typedef long long ll;
using namespace std;
const int maxn = 100005;
const int segn = 400005;
char s[maxn];
int n, m, op[maxn];
int pos[maxn], num[maxn];
map<int, int> mmp;
ll sum[segn][5], sz[segn];
void update(int p) {
    sz[p] = (sz[ls] + sz[rs]) % 5;
    for (int i = 0; i < 5; i++) {
        sum[p][i] = sum[ls][i] + sum[rs][(i + 5 - sz[ls]) % 5];
    }
}
void modify(int p, int l, int r, int x, int y, int z) {
    if (l == r) {
        if (z) {
            sum[p][0] = y;
            sz[p] = 1;
        } else {
            sum[p][0] = 0;
            sz[p] = 0;
        }
        return;
    }
    if (x <= md) {
        modify(ls, l, md, x, y, z);
    } else {
        modify(rs, md + 1, r, x, y, z);
    }
    update(p);
}
ll query() {
    return sum[1][2];
}
int main() {
    scanf("%d", &m);
    for (int i = 1; i <= m; i++) {
        scanf("%s", s);
        if (s[0] == 'a') {
            op[i] = 1;
            scanf("%d", num + i);
            mmp[num[i]] = 1;
        } else if (s[0] == 'd') {
            op[i] = 2;
            scanf("%d", num + i);
        }
    }
    for (map<int, int>::iterator it = mmp.begin(); it != mmp.end(); it++) {
        mmp[(*it).first] = ++n;
    }
    for (int i = 1; i <= m; i++) {
        if (num[i]) {
            pos[i] = mmp[num[i]];
        }
    }
    for (int i = 1; i <= m; i++) {
        if (op[i] == 1) {
            modify(1, 1, n, pos[i], num[i], 1);
        } else if (op[i] == 2) {
            modify(1, 1, n, pos[i], 0, 0);
        } else {
            printf("%I64d\n", query());
        }
    }
    return 0;
}

题解 II

分块,每一个块维护五个值,修改时在块内部暴力修改,查询时暴力统计每一个块。
时间复杂度: O(nn) O ( n n )


代码 II

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int block = 400;
const int maxn = 100005 + block;
char s[maxn];
int n, m, op[maxn], mrk[maxn];
int sz[maxn / block], pos[maxn], num[maxn];
map<int, int> mmp;
ll sum[maxn / block][5];
void modify(int x, int y, int z) {
    mrk[x] = y * z;
    int k = 0, b = (x - 1) / block + 1;
    sz[b] += z ? 1 : -1;
    for (int i = 0; i < 5; i++) {
        sum[b][i] = 0;
    }
    for (int i = (b - 1) * block + 1; i <= b * block; i++) {
        if (mrk[i]) {
            sum[b][k++ % 5] += mrk[i];
        }
    }
}
ll query() {
    int totsize = 0;
    ll result = 0;
    for (int b = 1, i = 1; i <= n; i += block, b++) {
        result += sum[b][(7 - totsize) % 5];
        totsize = (totsize + sz[b]) % 5;
    }
    return result;
}
int main() {
    scanf("%d", &m);
    for (int i = 1; i <= m; i++) {
        scanf("%s", s);
        if (s[0] == 'a') {
            op[i] = 1;
            scanf("%d", num + i);
            mmp[num[i]] = 1;
        } else if (s[0] == 'd') {
            op[i] = 2;
            scanf("%d", num + i);
        }
    }
    for (map<int, int>::iterator it = mmp.begin(); it != mmp.end(); it++) {
        mmp[(*it).first] = ++n;
    }
    for (int i = 1; i <= m; i++) {
        if (num[i]) {
            pos[i] = mmp[num[i]];
        }
    }
    for (int i = 1; i <= m; i++) {
        if (op[i] == 1) {
            modify(pos[i], num[i], 1);
        } else if (op[i] == 2) {
            modify(pos[i], 0, 0);
        } else {
            printf("%I64d\n", query());
        }
    }
    return 0;
}

总结

分块的时间复杂度有时看似没有线段树低,但是分块的常数空间复杂度比线段树小的多。所以分块也是数据结构题中的一种常用技巧。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值