补题链接: 码蹄集
一道经典线段树板子题。
区间修改01置换,区间查询子串权值。
唯一区别,权值要求的是相邻字符都不同所需修改的最小字符个数。
我们在线段树节点上分别维护当前连续区间:
奇数位是0的个数(j0),奇数位是1的个数(j1)。
偶数位是0的个数(o0),偶数位是1的个数(o1)。
以及当前区间的答案ans,是否往子区间延迟lazy。
考虑如何通过维护的信息进行pushup。
如图所示:
黑色三角:表示虚线左右子区间各自的奇数位置
红色三角:表示合并后奇数位置。
当左区间是奇数时,黑色三角=红色三角
要想变成10间断,要不变成101010...,要不变成0101010....
令 ls是左区间,rs是右区间。
如果变成101010,那就是ans=tr[ls].j0+tr[ls].o1+tr[rs].j0+tr[rs].o1
如果变成010101,那就是ans=总长度-(tr[ls].j0+tr[ls].o1+tr[rs].j0+tr[rs].o1)
取个max就行。
但当左区间不是奇数,黑色三角!=红色三角
如果变成101010,那就是ans=tr[ls].j0+tr[ls].o1+tr[rs].j1+tr[rs].o0
如果变成010101,那就是ans=总长度-(tr[ls].j0+tr[ls].o1+tr[rs].j1+tr[rs].o0)
到这里我们就解决了从子区间到大区间的pushup问题,代码如下所示。
void pushup(int p){int len=tr[p].r-tr[p].l+1;int lenls=tr[ls].r-tr[ls].l+1;if (lenls&1){int sum=tr[ls].j0+tr[ls].o1+tr[rs].j1+tr[rs].o0;tr[p].ans=min(sum,len-sum);tr[p].j0=tr[ls].j0+tr[rs].o0;tr[p].j1=tr[ls].j1+tr[rs].o1;tr[p].o0=tr[ls].o0+tr[rs].j0;tr[p].o1=tr[ls].o1+tr[rs].j1;}else{int sum=tr[ls].j0+tr[ls].o1+tr[rs].j0+tr[rs].o1;tr[p].ans=min(sum,len-sum);tr[p].j0=tr[ls].j0+tr[rs].j0;tr[p].j1=tr[ls].j1+tr[rs].j1;tr[p].o0=tr[ls].o0+tr[rs].o0;tr[p].o1=tr[ls].o1+tr[rs].o1;}
}
pushdown问题,其实比较常规,就是01置换,异或一下就行,代码如下所示。
void pushdown(int p){tr[ls].laz=tr[ls].laz^tr[p].laz;tr[rs].laz=tr[rs].laz^tr[p].laz;swap(tr[ls].j0,tr[ls].j1);swap(tr[ls].o0,tr[ls].o1);swap(tr[rs].j0,tr[rs].j1);swap(tr[rs].o0,tr[rs].o1);tr[p].laz=0;
}
当我们维护的东西不只是ans的时候,query需要返回一个结构体,因为当查询的x,y在左右区间都有的时候,需要向上手动合并。
全部问题解决完后,完整代码如下所示。
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+5;
#define ls p<<1
#define rs p<<1|1
struct tree{int l,r;int j0,j1,o0,o1;int laz,ans;
}tr[N];
char s[N];
int n,q;
void pushup(int p){int len=tr[p].r-tr[p].l+1;int lenls=tr[ls].r-tr[ls].l+1;if (lenls&1){int sum=tr[ls].j0+tr[ls].o1+tr[rs].j1+tr[rs].o0;tr[p].ans=min(sum,len-sum);tr[p].j0=tr[ls].j0+tr[rs].o0;tr[p].j1=tr[ls].j1+tr[rs].o1;tr[p].o0=tr[ls].o0+tr[rs].j0;tr[p].o1=tr[ls].o1+tr[rs].j1;}else{int sum=tr[ls].j0+tr[ls].o1+tr[rs].j0+tr[rs].o1;tr[p].ans=min(sum,len-sum);tr[p].j0=tr[ls].j0+tr[rs].j0;tr[p].j1=tr[ls].j1+tr[rs].j1;tr[p].o0=tr[ls].o0+tr[rs].o0;tr[p].o1=tr[ls].o1+tr[rs].o1;}
}
void pushdown(int p){tr[ls].laz=tr[ls].laz^tr[p].laz;tr[rs].laz=tr[rs].laz^tr[p].laz;swap(tr[ls].j0,tr[ls].j1);swap(tr[ls].o0,tr[ls].o1);swap(tr[rs].j0,tr[rs].j1);swap(tr[rs].o0,tr[rs].o1);tr[p].laz=0;
}
void build(int p,int l,int r){tr[p].l=l;tr[p].r=r;tr[p].laz=0;if (l==r){tr[p].j0=0;tr[p].j1=0;if (s[l]=='0') tr[p].j0=1;else if (s[l]=='1') tr[p].j1=1; tr[p].ans=0;return;}int mid=(l+r)>>1;build(ls,l,mid);build(rs,mid+1,r);pushup(p);
}
void update(int p,int x,int y){int l=tr[p].l,r=tr[p].r;if (x<=l&&r<=y){tr[p].laz=tr[p].laz^1;swap(tr[p].j0,tr[p].j1);swap(tr[p].o0,tr[p].o1);return;}if (tr[p].laz) pushdown(p);int mid=(l+r)>>1;if (x<=mid) update(ls,x,y);if (y>mid) update(rs,x,y);pushup(p);
}
tree query(int p,int x,int y){int l=tr[p].l,r=tr[p].r;if (x<=l&&r<=y){return tr[p];}if (tr[p].laz) pushdown(p);int mid=(l+r)>>1;if (y<=mid) return query(ls,x,y);else if (x>mid) return query(rs,x,y);else{struct tree T,a,b;a=query(ls,x,y);b=query(rs,x,y);T.l=a.l;T.r=b.r;int len=T.r-T.l+1;int lenls=a.r-a.l+1;if (lenls&1){int sum=a.j0+a.o1+b.j1+b.o0;T.ans=min(sum,len-sum);T.j0=a.j0+b.o0;T.j1=a.j1+b.o1;T.o0=a.o0+b.j0;T.o1=a.o1+b.j1;}else{int sum=a.j0+a.o1+b.j0+b.o1;T.ans=min(sum,len-sum);T.j0=a.j0+b.j0;T.j1=a.j1+b.j1;T.o0=a.o0+b.o0;T.o1=a.o1+b.o1;}return T;}
}
void co(){for (int i=1;i<=9;i++){cout<<tr[i].l<<" "<<tr[i].r<<" "<<tr[i].laz<<" "<<tr[i].ans<<"||";cout<<"S:";for (int j=1;j<=n;j++){cout<<s[j];}cout<<"\n";cout<<"奇数 0:"<<tr[i].j0<<"| 1:"<<tr[i].j1<<"|||";cout<<"偶数 0:"<<tr[i].o0<<"| 1:"<<tr[i].o1<<"\n";}
}
void work(){cin>>n>>q;for (int i=1;i<=n;i++) cin>>s[i];build(1,1,n);for (int i=1;i<=q;i++){int t,l,r;cin>>t>>l>>r;if (t==1) update(1,l,r);else cout<<query(1,l,r).ans<<"\n"; }
}
signed main(){ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);work();return 0;
}
/*
10101
000
*/