Codeforces Round #771 (Div. 2) E 题解
源自:Codeforces Round #771 (Div. 2) E Colorful Operations
题意:
你有一个长度为 \(n\) 的数组。对于所有在 \([1,n]\) 内的整数 \(i\),第 \(i\) 个元素 \(a_i\),一开始的值为 \(0\),颜色为 \(1\)。
你需要执行 \(q\) 次操作,每次操作为如下三种之一:
- \(\texttt{Color l r c}\) :将 \(a_l,a_{l+1},\cdots,a_r\)的颜色替换为 \(c\)。
- \(\texttt{Add c x}\):将数组中颜色为 \(c\) 的所有元素的值加上 \(x\)。
- \(\texttt{Query i}\) :输出 \(a_i\) 的值。
数据范围:
- \(1\leqslant n,q\leqslant 10^6\) 。
- 对于操作 1,\(1\leqslant l\leqslant r\leqslant n\),\(1\leqslant c\leqslant n\)
- 对于操作 2,\(1\leqslant c\leqslant n\),\(-10^9\leqslant x\leqslant 10^9\) 。
- 对于操作 3,\(1\leqslant i\leqslant n\) 。
分析
用常规的数据结构已经无法处理了,例如用线段树,操作1区间覆盖很简单,但是操作2无法在 \(O(logn)\) 时间内统计完成。
观察到操作1为区间染色,珂朵莉树能很好的处理这类问题。即使数据不随机,仅有操作1的话时间复杂度也能保证为
\(O(nlognlogn)\) 。
如何处理操作2成为本题的关键。
但发现,记录颜色为 \(c\) 的区间然后整段增减的操作是不可行的,因为珂朵莉树的操作只有染色时间复杂度是正确的 。
我们再将思路回到操作1:
- 列出 \((l,r)\) 之间的所有段
- 删除这些段 每段的颜色 \(i\)
- 插入 \([l,r]\)段,颜色为 \(j\)
在重新染色时,原有段的颜色会变为新颜色,而操作2也是针对于某种特定的颜色。
所以我们可以把对象从数组的元素变更为 颜色,对于每种颜色 设 \(val_{i}\)记录整体变化,而当属于这个颜色的某一段由操作1变为另一种颜色时,只需要对这段的元素
\[ans^{'}_{k} \leftarrow ans_{k} + val_{i} - val_{j}(i\,\text{表示旧颜色}\,j\,\text{表示新颜色})
\]
操作2只需变更 \(val_i\)
操作3答案为 \(ans_k + val_{c_k}\)
代码
// from :
#include<bits/stdc++.h>
using namespace std;
#define fastio cin.tie(0);cout.tie(0);ios::sync_with_stdio(false)
typedef pair<int, int> PII;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int dx[] = {0, 0, 1, -1}, dy[] = {1, -1, 0, 0};
void debug(int x = 0) {
cout << "--ok(" << x << ")" << endl;
}
void yes() {
cout << "YES" << "\n";
}
void no() {
cout << "NO" << "\n";
}
const int N = 1e6 + 5, M = 3e5 + 5, mod = 1e9 + 7;
/* cout << "\n";*/
struct P {
int l, r;
mutable int c;
P(const int &il, const int &ir, const int &ic) : l(il), r(ir), c(ic){}
inline bool operator<(const P &o) const { return l < o.l; }
};
set<P> s;
int n, m;
ll tag[N];
ll c[N];
void add(int x, ll y) {
for (; x <= n; x += x & -x) c[x] += y;
}
ll ask(int x) {
ll ans = 0;
for (; x; x -= x & -x) ans += c[x];
return ans;
}
auto split(int x) {
if (x > n) return s.end();
auto it = --s.upper_bound(P(x, 0, 0));
if (it -> l == x) return it;
int l = it -> l, r = it -> r;int c = it -> c;
s.erase(it);
s.insert(P(l, x - 1, c));
return s.insert(P(x, r, c)).first;
}
void assign(int l, int r, int c) {
auto itr = split(r + 1), itl = split(l);
for (; itl != itr; ++itl) {
add(itl -> l, tag[itl -> c]);
add(itl -> r + 1, -tag[itl -> c]);
}
itl = split(l);
s.erase(itl, itr);
add(l, -tag[c]);
add(r + 1, tag[c]);
s.insert(P(l, r, c));
}
int getc(int x) {
auto it = --s.upper_bound(P(x, 0, 0));
return it -> c;
}
void solve() {
cin >> n >> m;
s.insert(P(1, n, 1));
while (m--) {
string op; int l, r, c;
cin >> op >> l;
if (op == "Color") {
cin >> r >> c;
assign(l, r, c);
}
else if (op == "Add") {
cin >> r;
tag[l] += r;
}
else {
//cout << it -> c << "\n";
cout << ask(l) + tag[getc(l)] << "\n";
}
}
}
int main(){
fastio; cout << setprecision(10);
solve();
return 0;
}