坑点比较多的一道套路题。
首先一眼线段树,我们设 \(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<