Files
python/TangDou/AcWing/SegmentTree/P2787_XDS.cpp
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

123 lines
4.2 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, m, f[26];
char a[N]; // 初始化输入的字符串
// 多棵线段树法
#define ls (u << 1)
#define rs (u << 1 | 1)
#define mid ((l + r) >> 1)
struct Tree {
struct Node {
int l, r, len; // 区间范围
int sum; // 区间和
int tag; // 懒标记
} tr[N << 2];
void pushup(int u) {
tr[u].sum = tr[ls].sum + tr[rs].sum; // 汇总区间
}
// 修改u节点的懒标记和统计信息
void change(int u, int tag) {
tr[u].tag = tag;
if (tr[u].tag == 1) tr[u].sum = tr[u].len;
if (tr[u].tag == 0) tr[u].sum = 0;
}
void pushdown(int u) {
if (~tr[u].tag) {
// 向左儿子传递
change(ls, tr[u].tag);
// 向左儿子传递
change(rs, tr[u].tag);
// 终于完成向左右儿子传递懒标记的任务,将自己的懒标记清除
tr[u].tag = -1;
}
}
void build(int u, int l, int r) {
tr[u].l = l, tr[u].r = r, tr[u].len = r - l + 1; // 整体区间[1,n],管辖区间[l,r]
tr[u].tag = -1;
if (l == r) return;
build(ls, l, mid);
build(rs, mid + 1, r);
}
void modify(int u, int L, int R, int v) {
if (tr[u].tag == v) return; // 剪枝,否则无法通过12测试点
// 区间都修改为统一的值,如果上一次你修改成了1,本次还是修改成了1,那么只留同样的就可以了,不用再重新标识
// 完整命中,修改自己的懒标记和统计信息
int l = tr[u].l, r = tr[u].r;
if (l > R || r < L) return;
if (l >= L && r <= R) {
change(u, v);
return;
}
pushdown(u);
modify(ls, L, R, v), modify(rs, L, R, v);
pushup(u);
}
int query(int u, int L, int R) {
if (tr[u].sum == 0) return 0; // 整体加一块都是0,那你想查找子区间,也肯定是0,剪枝,不加这句,第12个测试点TLE
int l = tr[u].l, r = tr[u].r;
if (l > R || r < L) return 0;
if (l >= L && r <= R) return tr[u].sum; // 如果完整命中, 返回现成的
pushdown(u);
return query(ls, L, R) + query(rs, L, R);
}
} T[92]; // A~Z:65+26=91,所以开92
int main() {
#ifndef ONLINE_JUDGE
freopen("P2787.in", "r", stdin);
#endif
// 加快读入
ios::sync_with_stdio(false), cin.tie(0);
cin >> n >> m; // 字母个数,操作次数
for (int i = 1; i <= n; i++) cin >> a[i], a[i] = toupper(a[i]); // 读入每个字符,并且转成大写
for (char i = 'A'; i <= 'Z'; i++) T[i].build(1, 1, n); // 创建26个线段树
for (int i = 1; i <= n; i++) T[a[i]].modify(1, i, i, 1);
// 枚举字符串中每个字符,两个属性:哪根树,哪个位置,修改为1
while (m--) {
int op, l, r;
char k;
cin >> op >> l >> r;
if (op == 1) {
cin >> k;
k = toupper(k);
cout << T[k].query(1, l, r) << endl; // 查询第k棵线段树中[x,y]之间的区间和
}
if (op == 2) {
cin >> k;
k = toupper(k);
for (char i = 'A'; i <= 'Z'; i++) // 枚举每棵线段树
if (i == k)
T[i].modify(1, l, r, 1); // 修改的是第i根线段树,区间整体修改为1
else
T[i].modify(1, l, r, 0); // 其它线段树,区间整体修改为0
}
if (op == 3) {
for (char i = 'A'; i <= 'Z'; i++) {
f[i] = T[i].query(1, l, r); // 将第i棵线段树中[x,y]的区间和保存起来,放到f[i]中
T[i].modify(1, l, r, 0); // 放完了就修改为0,表示清除掉懒标记
}
// 利用区间修改和桶,完成类似于排序的操作
for (char i = 'A'; i <= 'Z'; i++)
if (f[i]) T[i].modify(1, l, l + f[i] - 1, 1), l += f[i];
}
}
return 0;
}