题解 洛谷P6373「StOI-1」IOI计数

坑点比较多的一道套路题。

首先一眼线段树,我们设 \(x\) 表示当前节点,\(L,R\) 分别表示当前节点的左右儿子。

那么 \(\operatorname{pushup}\) 转移就很显然了。

  • \(\operatorname{I_x=I_L+I_R}\)

  • \(\operatorname{O_x=O_L+O_R}\)

  • \(\operatorname{IO_x=IO_L+IO_R+I_L} \times \operatorname{O_R}\)

  • \(\operatorname{OI_x=OI_L+OI_R+O_L} \times \operatorname{I_R}\)

  • \(\operatorname{IOI_x=IOI_L+IOI_R+I_L} \times \operatorname{OI_R+IO_L} \times \operatorname{I_R}\)

用线段树维护上述 \(5\) 个值即可。

由于操作只包含单点修改,所以没必要写懒标记。

需要注意在询问的时候不能直接左右儿子答案相加,对于一个询问的区间我们考虑用 \(\operatorname{pushup}\) 的转移方式相加。实现的话只要重载一下 \(+\) 运算符就行了。

另外记得开 \(\operatorname{ll}\),还有一些细节参考代码吧(

\(\operatorname{Code:}\)

#include
using namespace std;
typedef long long ll;
const int N=500005;
inline int read() {
    register int s=0,f=1;
	register char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-f; ch=getchar();}
    while(isdigit(ch)) {s=(s<<1)+(s<<3)+(ch^48); ch=getchar();}
    return s*f;
}
int n,m;
string st;
struct node {
	int l,r;
	ll i,o,io,oi,ioi; //不开 ll 见祖宗 
	void mem () {i=o=io=oi=ioi=0;} //清零 
	node operator + (const node&a) const { //重载 + 运算符 
		node res;
        res.i = i+a.i;
		res.o = o+a.o;
		res.io = io+a.io+i*a.o;
	    res.oi = oi+a.oi+o*a.i;
		res.ioi = ioi+a.ioi+i*a.oi+io*a.i;
		return res;
    }
} tree[N<<2];
void pushup(int x) {
	int l=tree[x].l,r=tree[x].r; //临时储存 
	tree[x] = tree[x<<1]+tree[x<<1|1]; //两个儿子相加 
	tree[x].l=l,tree[x].r=r; //注意在结构体赋值中没有转移 [l,r],所以这里需要转移 
}
void build(int x,int l,int r) {
	tree[x].mem();
	tree[x].l=l,tree[x].r=r;
	if(l==r) {
		if(st[l]=='I') tree[x].i=1;
		if(st[l]=='O') tree[x].o=1;
		return;
	}
	int mid=(tree[x].l+tree[x].r)>>1;
	build(x<<1,l,mid);
	build(x<<1|1,mid+1,r);
	pushup(x);
}
node ask(int x,int l,int r) {
	if(l<=tree[x].l && tree[x].r<=r) return tree[x];
	int mid=(tree[x].l+tree[x].r)>>1;
	node res; res.mem(); //注意这里需要清零 
	if(l<=mid) res = res+ask(x<<1,l,r);
	if(r>mid) res = res+ask(x<<1|1,l,r); //区间相加,即结构体相加 
	return res;
}
void change(int x,int k,char c) {
	if(k==tree[x].l && tree[x].r==k) { 
		if(c=='I') tree[x].i=1,tree[x].o=0; //需要改变原来的值 
		if(c=='O') tree[x].o=1,tree[x].i=0; //单点修改 
		return;
	}
	int mid=(tree[x].l+tree[x].r)>>1;
	if(k<=mid) change(x<<1,k,c);
    else change(x<<1|1,k,c);
	pushup(x);
}
int main() {
    int n=read(),m=read();
	cin>>st; st=" "+st; //方便运算 
	build(1,1,n);
	while(m--) {
		int d=read(),k,l,r; char c;
		if(d==1) k=read(),cin>>c,change(1,k,c);
		else l=read(),r=read(),cout<

你可能感兴趣的:(题解 洛谷P6373「StOI-1」IOI计数)