题目大意:给定一个由01组成的字符串,对其某个区间进行反转(0变为1,1变为0)、查询1的个数两种操作,对于每次查询输出该区间1的个数。
思路:直接暴力做必定TLE,原本思路为:分块处理,设置num0与num1表分表示0与1的个数,设置一个vis数组表示被整体修改的次数。
1.当对块内部分元素进行修改时,若区间对应的vis为奇数,则再修改一次就改回来了,故不修改;为偶数则修改。
2.对整块进行修改时,交换num0与num1的值,vis++。
3.对块内部分元素进行查询时,若vis为奇数,则加上这部分元素中0的个数,否则加上1的个数。
4.对整块进行查询时,直接加上该块num1的值。
代码如下:
block b;
inline bool check(int i,int m) {return (b.vis[m]&1)?s[i]=='0':s[i]=='1';} //应该加一
inline void change(int i,int m) {
if(b.vis[m]&1) {
if(s[i]=='0') {
b.num1[m]--;
b.num0[m]++;
}
else {
b.num0[m]--;
b.num1[m]++;
}
}
else {
if(s[i]=='0') {
s[i]='1';
b.num1[m]++;
b.num0[m]--;
}
else {
s[i]='0';
b.num0[m]++;
b.num1[m]--;
}
}
}
inline void update(int l,int r) {
int st=l/b.block_size,end=r/b.block_size;
if(st==end) {
for(int i=l; i<=r; i++) change(i,st);
return;
}
if(l==b.st_index(st)) {
b.vis[st]++;
swap(b.num0[st],b.num1[st]);
}
else {
int tmp=b.end_index(st);
for(int i=l; i<=tmp; i++) change(i,st);
}
for(int i=st+1; i<end; i++) {
swap(b.num0[i],b.num1[i]);
b.vis[i]++;
}
if(l==b.end_index(end)) {
b.vis[end]++;
swap(b.num0[end],b.num1[end]);
}
else {
int tmp=b.st_index(end);
for(int i=tmp; i<=r; i++) change(i,end);
}
}
inline int query(int l,int r) {
int res=0;
int st=l/b.block_size,end=r/b.block_size;
if(st==end) {
for(int i=l; i<=r; i++) res+=(int)check(i,st);
return res;
}
if(l==b.st_index(st)) res+=b.num1[st];
else {
int tmp=b.end_index(st);
for(int i=l; i<=tmp; i++) res+=(int)check(i,st);
}
for(int i=st+1; i<end; i++) res+=b.num1[i];
if(r==b.end_index(end)) res+=b.num1[end];
else {
int tmp=b.st_index(end);
for(int i=tmp; i<=r; i++) res+=(int)check(i,end);
}
return res;
}
但以上方法只能得10分。经测试发现 先对整块进行修改,再对块内部分元素进行修改,再对块内部分元素进行查询就会出错,因为查询时不知道被查询的部分是否被“部分修改”过。
正确思路为: 1.进行“整块查询”时如果vis为奇数就计算0的个数,否则计算1的个数。
2.进行“整块修改”时避免改变num0与num1的值,只修改vis。
以上2点保证即使后面进行了“部分修改”也不会出错。
可以将vis设为bool型,并去掉num0减少空间使用。
代码如下:
#include <cstdio>
#include <algorithm>
#include <cmath>
const int M=(int)2e4+1,MAX_BLOCK=449;
using namespace std;
struct block {
int num1[MAX_BLOCK];
bool vis[MAX_BLOCK];
int block_size,num_block;
block() {
for(int i=0; i<MAX_BLOCK; i++) num1[i]=0,vis[i]=0;
}
inline int st_index(int m) {return m*block_size;}
inline int end_index(int m) {return st_index(m+1)-1;}
};
block b;
char a[M];
inline bool check(int i,int m) {return (b.vis[m])?a[i]=='0':a[i]=='1';} //应该加一
inline void change(int i,int m) {
if(a[i]=='0') {
a[i]='1';
b.num1[m]++;
}
else {
a[i]='0';
b.num1[m]--;
}
}
inline void update(int l,int r) {
int st=l/b.block_size,end=r/b.block_size;
if(st==end) {
for(int i=l; i<=r; i++) change(i,st);
return;
}
int tmp=b.end_index(st);
for(int i=l; i<=tmp; i++) change(i,st);
//整体修改时并不改变num0与num1的实际值
for(int i=st+1; i<end; i++) b.vis[i]=!b.vis[i];
tmp=b.st_index(end);
for(int i=tmp; i<=r; i++) change(i,end);
}
inline int query(int l,int r) {
int res=0;
int st=l/b.block_size,end=r/b.block_size;
if(st==end) {
for(int i=l; i<=r; i++) res+=(int)check(i,st);
return res;
}
int tmp=b.end_index(st);
for(int i=l; i<=tmp; i++) res+=(int)check(i,st);
for(int i=st+1; i<end; i++) {
if(!b.vis[i]) res+=b.num1[i];
else res+=b.block_size-b.num1[i];
}
tmp=b.st_index(end);
for(int i=tmp; i<=r; i++) res+=(int)check(i,end);
return res;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
scanf("%s",a);
b.block_size=(int)sqrt(n),b.num_block=n/b.block_size+1;
int S=b.block_size;
for(int i=0; i<n; i++) {
if(a[i]=='1') b.num1[i/S]++;
}
while(m--) {
int op,l,r;
scanf("%d%d%d",&op,&l,&r);
if(op==0) update(l-1,r-1);
else printf("%d\n",query(l-1,r-1));
}
return 0;
}