题目链接
【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;
}
总结
分块的时间复杂度有时看似没有线段树低,但是分块的常数和空间复杂度比线段树小的多。所以分块也是数据结构题中的一种常用技巧。