洛谷P2574
【题意】: AKN
觉得第一题太水了,不屑于写第一题,所以他又玩起了新的游戏。在游戏中,他发现,这个游戏的伤害计算有一个规律,规律如下:
拥有一个伤害串为长度为 的 串。
给定一个范围 ,伤害为伤害串的这个范围内中 的个数。
会被随机修改伤害串中的数值,修改的方法是把[l,r]中的所有数 上 (即 变 , 变 )。
AKN
想知道一些时刻的伤害,请你帮助他求出这个伤害。
【思路】: 线段树的题目。
记 表示区间 的长度, 表示区间 中 的个数。
首先考虑修改操作,我们发现修改操作会让每个数 变 , 变 ,所以总和(即 的个数) 。
举个例子,比如原来的数串为0001101
,其总和为
。修改后变为1110010
,总和为
。
然后,我们就可以很容易联想到线段树了。
【代码】:
const int N=2e5+100;
int Len[N<<2],sum[N<<2];
bool add[N<<2];int n,m;
inline void pushup(int o){
sum[o]=sum[o<<1]+sum[o<<1|1];
}//pushup(o):标记上传操作
inline void pushdown(int o){
sum[o<<1]=Len[o<<1]-sum[o<<1];
sum[o<<1|1]=Len[o<<1|1]-sum[o<<1|1];
add[o<<1]^=1;add[o<<1|1]^=1;add[o]=0;
}//pushdown(o):标记下传操作
int a[N],opt,x,y;string s;
void build(int o,int l,int r){
Len[o]=r-l+1;
if (l==r){
sum[o]=a[l];
add[o]=0;
return;
}
register int mid=(l+r)>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
pushup(o);return;
}//建树操作
void updata(int o,int l,int r,int p,int q){
if (l>q||r<p) return;
if (p<=l&&r<=q){
sum[o]=Len[o]-sum[o];
add[o]^=1;return;
}
if (add[o]) pushdown(o);
register int mid=(l+r)>>1;
updata(o<<1,l,mid,p,q);
updata(o<<1|1,mid+1,r,p,q);
pushup(o);return;
}//修改操作
int query(int o,int l,int r,int p,int q){
if (l>q||r<p) return 0;
if (p<=l&&r<=q) return sum[o];
if (add[o]) pushdown(o);
register int mid=(l+r)>>1;
register int answer=0;
answer+=query(o<<1,l,mid,p,q);
answer+=query(o<<1|1,mid+1,r,p,q);
return answer;
}//求和(即求答案)操作
#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
char c=0;int x=0;bool f=0;
while (!g(c)) f=c=='-',c=gc;
while (g(c)) x=x*10+c-48,c=gc;
return f?-x:x;
}//快读
int main(){
n=read();m=read();cin>>s;
for(int i=0;i<n;i++)
a[i+1]=s[i]-'0';
build(1,1,n);
for(int i=1;i<=m;i++){
opt=read();x=read();y=read();
if (opt) printf("%d\n",query(1,1,n,x,y));
else updata(1,1,n,x,y);
}
return 0;
}