Files
python/TangDou/AcWing/SegmentTree/P4979_ODT.cpp

97 lines
3.4 KiB
C++
Raw Normal View History

2025-08-30 18:35:01 +08:00
#include <bits/stdc++.h>
using namespace std;
// 柯朵莉树模板
struct Node {
int l, r, v;
bool operator<(const Node &b) const {
return l < b.l;
}
};
set<Node> s;
set<Node>::iterator split(int x) {
auto it = s.lower_bound({x});
if (it != s.end() && it->l == x) return it;
--it;
int L = it->l, R = it->r, V = it->v;
s.erase(it);
s.insert({L, x - 1, V});
return s.insert({x, R, V}).first;
}
void assign(int l, int r, int v) {
auto R = split(r + 1), L = split(l);
s.erase(L, R);
s.insert({l, r, v});
}
int main() {
#ifndef ONLINE_JUDGE
freopen("P4979.in", "r", stdin);
#endif
// 加快读入
ios::sync_with_stdio(false), cin.tie(0);
int n;
cin >> n; // n个字符,表示材料
int st = 1; // 区间起点start
char x;
cin >> x;
int a = x; // a:当前字符
int b = a; // b:当前的前一个字符
for (int i = 2; i <= n; i++) {
cin >> x;
a = x; // 读入一个字符
if (a != b) { // 如果两个字符不一致,说明开始的新的区间
s.insert({st, i - 1, b}); // 记录上一个区间
st = i; // 记录新的区间起点
}
b = a; // 修改前一个字符
}
s.insert({st, n, a}); // 将尾巴打扫干净
int m; // m次操作
cin >> m;
while (m--) {
cin >> x;
int l, r;
cin >> l >> r;
if (x == 'A') { // 替换材料
char op;
cin >> op;
assign(l, r, op); // 区间平推op
} else { // 询问[l,r]区间内的材料是否合法
auto R = split(r + 1), L = split(l);
/*
此时我们想进行一次推平操作,把[2,8]区间内的元素都改成666.首先我们发现,[8,10]是一个区间,
那么需要先split(9),把[8,8]和[9,10]拆成两个区间。
同理,原来的[1,2]区间,也需要拆成[1,1]和[2,2]。
*/
// 注意:当x=1或y=N时,你的程序不需要判断前后的情况,而只需要判断区间内的情况.
if (l > 1 && r < n) {
// 之所以要判断是不是l>1,是因为下面需要检查前面的区间块,而l=1时,是没有前面的区间块的
// 同理,r < n,是因为r=n时,是没有后面的区间块的
L--; // 前面的区间块,迭代器只能加加减减,有借有还
if (L->v == R->v) { // 如果前面区间的字符与后面区间的字符一样,根据题意,输出NO
puts("No");
continue;
}
L++; // 还原迭代器到原始L区间上,迭代器只能加加减减,有借有还
}
// 检查区间内的值是不是一致,L未能如期到达R,输出NO
auto t = L;
for (; L != R; L++) {
if (L->v != t->v)
break;
}
if (L != R)
puts("No");
else
puts("Yes");
// 检查是同一个区域的进行整理,合并,起到优化作用,不加上会TLE三个点
assign(t->l, (--L)->r, t->v); // --L是最后一个匹配位置,合并[t,--L]
}
}
return 0;
}