https://www.luogu.org/problemnew/show/P2574
首先,由于是区间修改和区间查询,并且数据范围还这么大,所以需要使用线段树来维护。 读入的数都是0或1且修改操作为异或1的话,那么lazytag和线段树的维护就很好想了。
因为01=1,11=0,所以本题如果对同一个区间连续异或1两次,得到的结果和没有异或是一样的。
对于线段树中不是叶子结点的节点,它的值是它的左右子树的和,也就是它对应的这段区间里面1的个数(因为只有0和1,所以它的值是几就有几个1)。
如果对于一段区间,长度为len,有n个1,那么就有len-n个0。相应地,对这段区间异或1之后,它的1的个数就变成了len-n。
这就是这个题的基本思路。还有一些需要注意的点:
读入的时候一定要按照字符读入,否则就直接读入了所有的数(因为没有空格)。
如果说区间长度并不能整除以2,那么就把除以二向下取整丢给左儿子,然后把剩下的丢给右儿子。
#include
#include
#include
#include
using namespace std;
const int M=500500;
struct Tree
{
int son[2],bond[2];
int la,asum;
}t[M];
int n,m;
string s;
int num[M],cnt=1,rt=0;
void up(int x)
{
int ls=t[x].son[0];
int rs=t[x].son[1];
t[x].asum=t[ls].asum+t[rs].asum;
t[x].bond[0]=t[ls].bond[0];
t[x].bond[1]=t[rs].bond[1];
return ;
}
void built(int l,int r,int cur)
{
if (l==r)
{
t[cur].asum=num[l];
t[cur].bond[0]=t[cur].bond[1]=l;
t[cur].son[0]=t[cur].son[1]=-1;
return ;
}
t[cur].son[0]=cnt++;
t[cur].son[1]=cnt++;
int mid=(l+r)>>1;
built(l,mid,t[cur].son[0]);
built(mid+1,r,t[cur].son[1]);
up(cur);
return ;
}
void down(int x)
{
if (t[x].la%2==0) return ;
int ls=t[x].son[0];
int rs=t[x].son[1];
t[ls].asum=(t[ls].bond[1]-t[ls].bond[0]+1-t[ls].asum);
t[rs].asum=(t[rs].bond[1]-t[rs].bond[0]+1-t[rs].asum);
t[ls].la++;
t[rs].la++;
t[x].la=0;
return ;
}
void date(int l,int r,int val,int cur)
{
if (l<=t[cur].bond[0]&&t[cur].bond[1]<=r)
{
t[cur].asum=(t[cur].bond[1]-t[cur].bond[0]+1-t[cur].asum);
t[cur].la++;
return ;
}
down(cur);
int mid=(t[cur].bond[0]+t[cur].bond[1])>>1;
if (l<=mid) date(l,r,val,t[cur].son[0]);
if (r>mid) date(l,r,val,t[cur].son[1]);
up(cur);
return ;
}
int query(int l,int r,int x)
{
if (l<=t[x].bond[0]&&t[x].bond[1]<=r) return t[x].asum;
down(x);up(x);int tot=0;
int mid=(t[x].bond[0]+t[x].bond[1])>>1;
if (l<=mid) tot+=query(l,r,t[x].son[0]);
if (r>mid) tot+=query(l,r,t[x].son[1]);
return tot;
}
int main()
{
scanf("%d %d",&n,&m);cin>>s;
for (int i=1;i<=n;i++) num[i]=s[i-1]-'0';
built(1,n,rt);
while (m--)
{
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
if (a==0)
{
date(b,c,1,rt);
}
else cout<