「一題多解」【CodeForces 85D】Sum of Medians(線段樹 / 分塊)
阿新 • • 發佈:2019-02-08
題目連結
題目大意
實現一個,支援插入,刪除,求。注意,中的數在任何時刻都應該是排好序的。
題解 I
首先想到離線處理,每一個數字有一個離散化後的編號。
線段樹的每一個節點維護五個值,分別表示,更新世晚期按照左子樹更新就好了。
時間複雜度:。
程式碼 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
分塊,每一個塊維護五個值,修改時在塊內部暴力修改,查詢時暴力統計每一個塊。
時間複雜度:。
程式碼 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;
}
總結
分塊的時間複雜度有時看似沒有線段樹低,但是分塊的常數和空間複雜度比線段樹小的多。所以分塊也是資料結構題中的一種常用技巧。