洛谷 P3870 [TJOI2009] 开关


线段树复健。因为灯只能开或者关,所以区间和修改可以变成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是要一路跑到叶子节点,然后再后序遍历回去更新所有父节点,有了懒标记现在只要跑到询问区间就可以截止。

猜你喜欢

转载自blog.csdn.net/qq_17807067/article/details/129526806