You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123 lines
4.2 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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;
}