ODT
CF1638E Colorful Operations
从这个题目开始,提取关键词:颜色、操作序列,我们很快就能想到建立颜色个数个线段树,但当颜色个数比较大时,我们不可能建立那么多线段树,考虑到区间覆盖,我们可以思考的是,是否可以用一个规则来概括整个区间,那么在这里,我们就认为整个区间都是一个颜色。
ODT
我们通过用平衡树来描述区间,可以用 \(map<int, int>\) 来表示区间左端点和区间中颜色,在操作中我们可以像 FHQ 那样将区间分裂出来进行处理。
因为思路比较简单,所以直接上代码,时间复杂度我不会证
auto split(int p) {
auto it = prev(odt.upper_bound(p));
return odt.insert(it, make_pair(p, it->second));
}
void assign(int l, int r, int c) {
split(l), split(r + 1);
for (auto it = odt.find(l);it->first != r + 1; it = odt.erase(it)) {
modify(it->first, tag[it->second]);
modify(next(it)->first, -tag[it->second]);
}
odt[l] = c, modify(l, -tag[c]), modify(r + 1, tag[c]);
}
思路
我们考虑对于这道题目,有三种操作
- 区间颜色改变,直接对应 \(assign\) 操作
- 对于有特定颜色的节点进行加减操作
- 单点查询
对于 2 操作,由于特定颜色节点不一定抱团,所以我们使用懒标记,不过懒标记在什么时候需要改变呢?
在 1 操作中,会将结构进行改变,在改变过程中,需将标记下放到每一个区间中,这里需要用区间修改,用树状数组,在颜色修改完毕后,由于改后颜色在过去的 tag 不应该影响到现在的所以需要减掉以抵消,在树状数组上减即可
code
#include <iostream>
#include <map>
#define int long long
#define lb(x) (x & (-x))
using namespace std;
const int MaxN = 1e6 + 10;
int tag[MaxN], d[MaxN], n, q;
map<int, int> odt;
string op;
void modify(int k, int x) {
for (k; k <= n; d[k] += x, k += lb(k)) {
}
}
int query(int k, int res = 0) {
for (k; k; res += d[k], k -= lb(k)) {
}
return res;
}
auto split(int p) {
auto it = prev(odt.upper_bound(p));
return odt.insert(it, make_pair(p, it->second));
}
void assign(int l, int r, int c) {
split(l), split(r + 1);
for (auto it = odt.find(l);it->first != r + 1; it = odt.erase(it)) {
modify(it->first, tag[it->second]);
modify(next(it)->first, -tag[it->second]);
}
odt[l] = c, modify(l, -tag[c]), modify(r + 1, tag[c]);
}
int get(int c) {
auto it = prev(odt.upper_bound(c));
return it->second;
}
signed main() {
cin >> n >> q;
odt[0] = -1, odt[n + 1] = -1, odt[1] = 1;
for (int l, r, c, x; q; q--) {
cin >> op;
if (op == "Color") {
cin >> l >> r >> c;
assign(l, r, c);
} else if (op == "Add") {
cin >> c >> x;
tag[c] += x;
} else {
cin >> x;
cout << query(x) + tag[get(x)] << endl;
}
}
return 0;
}

浙公网安备 33010602011771号