CSUOJ 1258 维护序列

维护序列

线段树题目。此题的更新必须要更新到叶节点,否则的话,会出现错误。对于一个int范围内的数字,更新若干次(最多32次)后必然会变成0,那么此时在更新就没有意义了。那么我们可以用lazy标记,当某个区间的数字全部变为0后,我们将这个区间做上标记。那么这个区间就不可能在被更新,这样就可以提高效率,减去不必要的更新。对于将一个数字的二进制位最低位1变成0,在做树状数组题目的时候我们常用一个lowbit()函数来获取某个数字二进制位最低位1,这里我们也用这个来实现,我们先取出对应的最低位,然后在进行异或操作就可以了,也可以将取出的数字进行一次取反,然后在与原数字进行与操作。即:a&(~(a&(-a)))

#include <iostream>
#include <string.h>
#include <stdio.h>

using namespace std ;

#define MAXN 10005

#define M(x , y) (x+y)>>1
#define L(x) x<<1
#define R(x) x<<1|1

struct Tree{
    int l ;
    int r ;
    int sum ;
    int lazy;
}tree[MAXN * 4] ;

int n ;
int m ;

inline void pushup(int c){
    int l = L(c) ;
    int r = R(c) ;
    tree[c].sum = tree[l].sum ^ tree[r].sum ;
    if(tree[l].lazy && tree[r].lazy)
        tree[c].lazy = 1 ;
}

void build(int l ,int r , int c){
    tree[c].l = l ;
    tree[c].r = r ;
    tree[c].sum = 0  ;
    tree[c].lazy = 0 ;
    if(l == r)
        return ;
    build( l , M(l, r) , L(c)) ;
    build( (M(l , r)) + 1 , r , R(c)) ;
}

void insert(int v , int l , int c){
    if(tree[c].l == l && tree[c].r==l){
        tree[c].sum = v ;
        if(!v)
            tree[c].lazy = 1 ;
        return ;
    }
    int m = M(tree[c].l , tree[c].r) ;
    if(l <= m){
        insert(v , l , L(c)) ;
    }
    else{
        insert(v , l , R(c)) ;
    }
    pushup(c) ;
}

void update(int l , int r , int c){
    if(tree[c].lazy)
        return  ;

    if(l == r && tree[c].l==tree[c].r && l==tree[c].l){
        if(tree[c].sum != 0){
            int k = tree[c].sum & (-tree[c].sum) ;
            tree[c].sum = tree[c].sum^k ;

            if(!tree[c].sum){
                tree[c].lazy = 1 ;
            }
        }
        return ;
    }
    int m = M(tree[c].l , tree[c].r) ;

    if(l==tree[c].l && r == tree[c].r){
        update(l , m , L(c) ) ;
        update(m + 1 , r , R(c)) ;
    }
    else if(m >= r){
        update(l  , r , L(c)) ;
    }
    else if(m < l){
        update(l , r ,  R(c)) ;
    }
    else {
        update(l , m , L(c)) ;
        update(m+1 , r , R(c)) ;
    }
    pushup(c) ;
}

void query(int &ans , int l , int r , int c){
    if(tree[c].lazy){
        ans ^= 0 ;
        return ;
    }
    if(tree[c].l == l && tree[c].r == r){
        ans ^= tree[c].sum ;
        return ;
    }
    int m = M(tree[c].l , tree[c].r) ;
    if(r <= m){
        query(ans , l , r , L(c)) ;
    }
    else if(l > m){
        query(ans , l , r , R(c)) ;
    }
    else{
        query(ans , l     , m , L(c)) ;
        query(ans , m + 1 , r , R(c)) ;
    }
}

int main(){
    while(scanf("%d%d" , &n , &m) != EOF){

        int x ;
        int y ;
        int k ;
        build(1 , n , 1) ;
        for(int i = 1 ; i <= n ; i ++){
            scanf("%d" , &x) ;
            insert(x , i , 1 ) ;
        }

        for(int i = 1 ; i <= m ; i ++){
            scanf("%d%d%d" , &k , &x , &y) ;
            if(k == 1){
                update(x , y , 1) ;
            }
            else{
                int ans = 0 ;
                query(ans , x , y , 1) ;
                printf("%d\n" , ans) ;
            }
        }
    }
    return 0 ;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值