1. 程式人生 > >「一題多解」【CodeForces 85D】Sum of Medians(線段樹 / 分塊)

「一題多解」【CodeForces 85D】Sum of Medians(線段樹 / 分塊)

題目連結

題目大意

實現一個set,支援插入,刪除,求a5k+3。注意,set中的數在任何時刻都應該是排好序的。

題解 I

首先想到離線處理,每一個數字有一個離散化後的編號。
線段樹的每一個節點維護五個值,分別表示a5k+t,更新世晚期按照左子樹size更新就好了。
時間複雜度:O(nlogn)

程式碼 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)

程式碼 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;
}

總結

分塊的時間複雜度有時看似沒有線段樹低,但是分塊的常數空間複雜度比線段樹小的多。所以分塊也是資料結構題中的一種常用技巧。