其他分享
首页 > 其他分享> > 「StOI-1」IOI计数

「StOI-1」IOI计数

作者:互联网

[洛谷链接](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);//因为传的结构体所以要加具体的变量
		}
	}
} 

标签:io,oi,int,mid,计数,StOI,IOI,now,lx
来源: https://www.cnblogs.com/lakafl/p/14365180.html