[洛谷链接](https://www.luogu.com.cn/problem/P6373)
题目背景
蒻L_C_A想了解一下IOI,可他太菜了,看不懂题目,只会数数。
题目描述
给定一个长度为 nn 字符串 SS ,同时进行 mm 次操作:
操作1:11 xx cc 表示将第 xx 个字符改为 cc( cc 只会为 I 或 O )。
操作2:22 ll rr 询问字符串 SS 中有多少对三元组 (i,j,k) (i,j,k) 满足:
S_{i}=S i = I ,S_{j} = S j = O, S_{k} = S k = I
并且 l ≤ i < j < k ≤ rl ≤ i < j < k ≤ r 。
输入格式
输入第一行两个正整数 nn 和 mm 。
接下来一行是长度为 nn 的字符串 ss ,接下来 mm 行是操作。
含义均如题。
输出格式
输出若干行:对于所有操作2,输出查询的答案,要求每个答案之间换行。
输入输出样例
输入1
4 3
IOOI
2 1 4
1 1 O
2 1 2
输出1
2
0
输入2
10 10
IIOOIOIIIO
1 1 I
2 1 7
1 5 O
2 5 9
1 4 I
1 10 I
2 1 10
2 5 10
2 2 8
2 3 9
输出2
11
0
34
0
11
6
分析:根据在一个点修改I, O,在一个区间内查询IOI,可知这题是道单点修改,区间查询的题,但是IOI并不是一个单独的数据,可以有很多组合,所以我们应该统计每一种组合的数量,具体的:
(为组合后的结构体,x,y分别是左右两边的结构体)
z.i = x.i + y.i;
z.o = x.o + y.o;
z.io = x.io + y.io + x.i * y.o;
z.oi = x.oi + y.oi + x.o * y.i;
z.ioi = x.ioi + y.ioi + x.io * y.i + x.i * y.oi;
io,oi首先单独加上左右单独的数量,再加上左右组合的数量(乘法原理)
IOI亦是同理,左右单独,再加上左右组合。
注意:再ask时一定不要,把左右的结构体赋给中间的,可能有一边没有访问(zz错误,还卡了0.5h)
#include<cstdio>
#define int long long
struct lx {
int now, l, r;
int i, o, oi, io, ioi;
}a[2000005];
char h[500005];
lx put(lx w, lx x, lx y) {
lx z;
z.l = w.l; z.r = w.r; z.now = w.now; //单独开的结构体注意要传now,l,r基本信息
z.i = x.i + y.i;
z.o = x.o + y.o;
z.io = x.io + y.io + x.i * y.o;
z.oi = x.oi + y.oi + x.o * y.i;
z.ioi = x.ioi + y.ioi + x.io * y.i + x.i * y.oi;
return z;
}
void build(int now, int l, int r) {
a[now].l = l; a[now].r = r;
// printf("%d %d %d\n", now, l, r);
if(l == r) return; int mid = (l + r) >> 1;
build(now * 2, l, mid);
build(now * 2 + 1, mid + 1, r);
}
void update(int now, int l, char s, bool flag) {
lx x, y;
if(a[now].l == a[now].r) {
a[now].i = 0; a[now].o = 0;
if(s == 'O') {
a[now].o = 1; h[l] = 'O';
} else if(s == 'I') {
a[now].i = 1; h[l] = 'I';
} return;
} int mid = (a[now].l + a[now].r) >> 1;
if(mid >= l) {
update(now * 2, l, s, flag);
} else {
update(now * 2 + 1, l, s, flag);
} a[now] = put(a[now], a[now * 2], a[now * 2 + 1]);
return ;
}
lx ask(int now, int l, int r) {
lx x, y;
if(l <= a[now].l && a[now].r <= r) return a[now];
x.i = 0; x.io = 0; x.oi = 0; x.o = 0; x.ioi = 0;
y.i = 0; y.io = 0; y.oi = 0; y.o = 0; y.ioi = 0;
int mid = (a[now].l + a[now].r) >> 1;
if(mid >= l) {
x = ask(now * 2, l, r);
} if(mid < r) {
y = ask(now * 2 + 1, l, r);
} return put(a[now], x, y);//一定要直接传不要赋值
}
signed main() {
char s[5];
int n, m, c, l, r;
scanf("%lld %lld", &n, &m);
build(1, 1, n);//建树
scanf("%s", h + 1);
for(int i = 1; i <= n; i ++) { update(1, i, h[i], 1); } //我是单独再把原数组添加进树,可以在建树时加
for(int i = 1; i <= m; i ++) {
scanf("%lld", &c);
if(c == 1) {
scanf("%lld %s", &l, s);
update(1, l, s[0], 0);
// for(int i = 1; i <= n; i ++) {
// printf("%c", h[i]);
// } printf("\n");
} else {
scanf("%lld %lld", &l, &r);
printf("%lld\n", ask(1, l, r).ioi);//因为传的结构体所以要加具体的变量
}
}
}