线段树复健。因为灯只能开或者关,所以区间和修改可以变成sum=sum-(r-l+1),加上初始状态全部是0,所以不需要建树,直接跑操作就行。
TLE代码(20分)无懒标记
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+1;
struct {
int sum;
}t[N<<2];
inline int read(){//所给数据只有正整数,可以省去x*f
int x=0;char ch=getchar();
while(ch<'0' or ch>'9')ch=getchar();
while(ch>='0' and ch<='9')x=(x<<3)+(x<<1)+(ch xor 48),ch=getchar();
return x;
}
inline int lc(int x){
return x<<1;
}
inline int rc(int x){
return x<<1|1;
}
inline void update(int i){
t[i].sum=t[lc(i)].sum+t[rc(i)].sum;
}
void turn(int l,int r,int i,int x,int y){
if(l==r){
if(l>=x&&l<=y)t[i].sum^=1;//半加反转状态
return;
}
int mid=(l+r)>>1;
turn(l,mid, lc(i),x,y);
turn(mid+1,r, rc(i),x,y);
update(i);
}
int query(int l,int r,int i,int x,int y){
int ans=0;
if(l>=x&&r<=y)return t[i].sum;
int mid=(l+r)>>1;
if(x<=mid)ans+=query(l,mid, lc(i),x,y);
if(y>mid)ans+=query(mid+1,r, rc(i),x,y);
return ans;
}
int n,m,c,a,b;
int main(){
n=read(),m=read();
for(int i=1;i<=m;++i){
c=read(),a=read(),b=read();
if(c==0){//翻转[a,b]区间的灯
turn(1,n,1,a,b);
}else{//查询[a,b]有多少开着的灯
cout<<query(1,n,1,a,b)<<endl;
}
}
return 0;
}
超时了那就要修改函数为带懒标记的版本。
AC代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+1;
struct {
int sum,tag;
}t[N<<2];
inline int read(){
int x=0;char ch=getchar();
while(ch<'0' or ch>'9')ch=getchar();
while(ch>='0' and ch<='9')x=(x<<3)+(x<<1)+(ch xor 48),ch=getchar();
return x;
}
inline int lc(int x){
return x<<1;
}
inline int rc(int x){
return x<<1|1;
}
inline void update(int i){
t[i].sum=t[lc(i)].sum+t[rc(i)].sum;
}
inline void download(int l,int r,int i){
if(t[i].tag){
int mid=(l+r)>>1;
t[lc(i)].sum=(mid-l+1)-t[lc(i)].sum;
t[rc(i)].sum=(r-mid)-t[rc(i)].sum;
t[lc(i)].tag^=1;
t[rc(i)].tag^=1;
t[i].tag^=1;
}
}
void turn(int l,int r,int i,int x,int y){
if(l>=x&&r<=y){
t[i].sum=(r-l+1)-t[i].sum;
t[i].tag^=1;
return;
}
int mid=(l+r)>>1;
download(l,r,i);
if(x<=mid)turn(l,mid, lc(i),x,y);
if(y>mid)turn(mid+1,r, rc(i),x,y);
update(i);
}
int query(int l,int r,int i,int x,int y){
int ans=0;
if(l>=x&&r<=y)return t[i].sum;
int mid=(l+r)>>1;
download(l,r,i);
if(x<=mid)ans+=query(l,mid, lc(i),x,y);
if(y>mid)ans+=query(mid+1,r, rc(i),x,y);
return ans;
}
int n,m,c,a,b;
int main(){
n=read(),m=read();
for(int i=1;i<=m;++i){
c=read(),a=read(),b=read();
if(c==0){//翻转[a,b]区间的灯
turn(1,n,1,a,b);
}else{//查询[a,b]有多少开着的灯
cout<<query(1,n,1,a,b)<<endl;
}
}
return 0;
}
原本turn是要一路跑到叶子节点,然后再后序遍历回去更新所有父节点,有了懒标记现在只要跑到询问区间就可以截止。