zoukankan      html  css  js  c++  java
  • 线段树与位运算

    链接:https://ac.nowcoder.com/acm/contest/283/J
    来源:牛客网

    RMQ
    时间限制:C/C++ 3秒,其他语言6秒
    空间限制:C/C++ 131072K,其他语言262144K
    64bit IO Format: %lld

    题目描述

    按位或运算:处理两个长度相同的二进制数,两个相应的二进位中只要有一个为1,该位的结果值为1。例如5 or 3 = 7

            0101(十进制5)
         OR 0011(十进制3)
          = 0111(十进制7)
    
    —— 引用自 位运算——维基百科
    小姐姐想要一种数据结构,支持如下操作:
    对于一个整数数组:    
    1. 给定L和R,输出[L,R]中元素的和
    2. 给定L,R和X,将[L,R]中每个元素与X进行按位或运算
    3. 数组索引从1开始
    按位或在CC++、Java、Python中为'|'运算符

    输入描述:

    第一行为两个整数 n 和 m,表示数组元素个数和操作的次数
    第二行有n个整数,第i个表示数组array中第i个元素的值array[i]
    接下来m行,每行只有两种可能:
    1. SUM L R
    表示对[L,R]的元素求和并输出
    2. OR L R X
    表示对[L,R]的每一个元素与X进行按位或运算,L、R为base 1的数字序号
    数据满足:
    1n,m2000001≤n,m≤200000
    1arrayi,X10000001≤arrayi,X≤1000000
    1L,Rn1≤L,R≤n
     

    输出描述:

    对于每个SUM操作,在一行内输出该操作的结果。
    示例1

    输入

    复制
    5 3
    1 2 3 4 5
    SUM 1 4
    OR 2 5 10
    SUM 1 4

    输出

    复制
    10
    36

    说明

    在第一个SUM操作时数组a为[1, 2, 3, 4, 5],因此[1,4]和为10

    在第二个SUM操作时数组a为[1, 10, 11, 14, 15],因此[1,4]和为36

    线段树的区间修改里有对其区间数全部进行" ^ "或者" | "位运算,我们可以利用这两个位运算的特性来进行修改(这两个都只需记录1的个数即可)。

    “  |   ”:由于它只要二进制上有1则为1,所以我们只需知道它二进制有无1即可。有则改为区间长度个数的1。

    “  ^  ":由于它二进制上两者不同才为1,所以我们只需知道它二进制有无1即可。有则改为区间长度-之前存在1的个数的1。

    //https://ac.nowcoder.com/acm/contest/283/J
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=2e5+5;
    const int maxnbit=27;
    const string SUM="SUM";
    const string OR="OR";
    
    int n,m,val;
    struct Tree{
        int lazy;
        int digit[maxnbit];
    }tree[maxn<<2];
    
    void up(int k){
        for(int i=0;i<25;i++){///子树1个数相加等于父亲的1的个数
            tree[k].digit[i]=tree[k<<1].digit[i]+tree[k<<1|1].digit[i];
        }
    }
    
    void pushdown(int k,int l,int r){///下传
        if(l==r) return ;
        tree[k<<1].lazy|=tree[k].lazy;
        tree[k<<1|1].lazy|=tree[k].lazy;
        ///tree[k<<1].lazy^=tree[k].lazy;
        ///tree[k<<1|1].lazy^=tree[k].lazy;
        int mid=(l+r)>>1;
        for(int i=0;i<25;i++){
            if(tree[k].lazy&(1<<i)){
                tree[k<<1].digit[i]=mid-l+1;
                tree[k<<1|1].digit[i]=r-mid;
                ///tree[k<<1].digit[i]=mid-l+1-digit[i];
                ///tree[k<<1|1].digit[i]=r-mid-digit[i];
            }
        }
        tree[k].lazy=0;
    }
    
    void build(int k,int l,int r){
        tree[k].lazy=0;
        if(l==r){
            scanf("%d",&val);
            for(int i=0;i<25;i++){
                if(val&(1<<i)){
                    tree[k].digit[i]=1;
                }else{
                    tree[k].digit[i]=0;
                }
            }
            return ;
        }
        int mid=(l+r)>>1;
        build(k<<1,l,mid);
        build(k<<1|1,mid+1,r);
        up(k);
    }
    
    void update(int k,int l,int r,int L,int R,int X){///[L,R]|X
        if(l>=L&&r<=R){
            tree[k].lazy|=X;
            ///tree[k].lazy^=X;
            for(int i=0;i<25;i++){///更改第k个节点的第i位上1的个数
                if(X&(1<<i)){
                    tree[k].digit[i]=r-l+1;
                }
            }
            return ;
        }
        if(tree[k].lazy) pushdown(k,l,r);
        int mid=(l+r)>>1;
        if(mid>=R) update(k<<1,l,mid,L,R,X);
        else if(mid<L) update(k<<1|1,mid+1,r,L,R,X);
        else{
            update(k<<1,l,mid,L,R,X);
            update(k<<1|1,mid+1,r,L,R,X);
        }
        up(k);
    }
    
    ll ans;
    
    void query(int k,int l,int r,int L,int R){
        if(l>=L&&r<=R){
            for(int i=0;i<25;i++){///计算答案
                ans+=1ll*tree[k].digit[i]*(1<<i);
            }
            return ;
        }
        if(tree[k].lazy) pushdown(k,l,r);
        int mid=(l+r)>>1;
        if(mid>=R) query(k<<1,l,mid,L,R);
        else if(mid<L) query(k<<1|1,mid+1,r,L,R);
        else{
            query(k<<1,l,mid,L,R);
            query(k<<1|1,mid+1,r,L,R);
        }
        up(k);
    }
    
    int main(){
        string q;
        int L,R,X;
        scanf("%d%d",&n,&m);
        build(1,1,n);
        while(m--){
            cin>>q;
            if(q==SUM){
                ans=0;
                scanf("%d%d",&L,&R);
                query(1,1,n,L,R);
                printf("%lld
    ",ans);
            }else if(q==OR){
                scanf("%d%d%d",&L,&R,&X);
                update(1,1,n,L,R,X);
            }
        }
        return 0;
    }
  • 相关阅读:
    GDOI 2020 赛前两周模拟总结
    猫树模板
    LOJ#2023. 「AHOI / HNOI2017」抛硬币(OGF+ExLucas+Crt)
    扩展Lucas定理及其优化
    LOJ#2018. 「AHOI / HNOI2017」单旋(平衡树模拟+set+线段树)
    LOJ #2008. 「SCOI2015」小凸想跑步(半平面交)
    [TJOI2018]游园会
    [Ynoi2018]未来日记
    「雅礼集训 2018 Day7」B
    「雅礼集训 2018 Day7」A
  • 原文地址:https://www.cnblogs.com/liuzuolin/p/10856068.html
Copyright © 2011-2022 走看看