CF 242E [线段树区间亦或+求和]

CF 242E [线段树区间亦或+求和]

题目链接

题意:给你n个数,有两种操作,op=1,对从l到r的数求和,op=2,对从l到r的值xor val。
思路:由于亦或是位运算,我们可以考虑位运算的关系,
1 xor 1=0,
0 xor 1=1,
1 xor 0=1;
0 xor 0=0;
可以看出0 xor x=x;
1 xor 1=0,1 xor 0=1,就是1变0,0变1;
我们把每个数都拆成2进制,由于是求和,只考虑每一位上1的个数,按位展开求和即可。那么我们亦或一个数也是只亦或位=1的时候,开20颗线段树就可以维护每一位的状态。
其实就是普通的区间更新区间求和问题,只不过线段树是二维的
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int b[maxn];
struct node
{
    int l,r,flag,sum;
} a[21][maxn<<2];
void pushup(int i,int id)
{
    a[id][i].sum=a[id][i*2].sum+a[id][i*2+1].sum;
}
void build(int i,int l,int r,int id)
{
    a[id][i].l=l,a[id][i].r=r,a[id][i].flag=0;
    if(l==r)
    {
        if((b[l]>>id)&1)
            a[id][i].sum=1;
        else
            a[id][i].sum=0;
        return ;
    }
    int mid=(l+r)/2;
    build(i*2,l,mid,id);
    build(i*2+1,mid+1,r,id);
    pushup(i,id);
}
void pushdown(int i,int id)
{
    if(a[id][i].flag==1)
    {
        a[id][i].flag=0;
        a[id][i*2].flag^=1,a[id][i*2+1].flag^=1;
        a[id][i*2].sum=(a[id][i*2].r-a[id][i*2].l+1)-a[id][i*2].sum;
        a[id][i*2+1].sum=(a[id][i*2+1].r-a[id][i*2+1].l+1)-a[id][i*2+1].sum;
    }
}
void update(int i,int L,int R,int l,int r,int id)
{
    if(l<=L&&r>=R)
    {
        a[id][i].sum=(R-L+1)-a[id][i].sum;
        a[id][i].flag^=1;
        return ;
    }
    pushdown(i,id);
    int mid=(L+R)/2;
    if(l<=mid)
        update(i*2,L,mid,l,r,id);
    if(r>mid)
        update(i*2+1,mid+1,R,l,r,id);
    pushup(i,id);
}
ll query(int i,int L,int R,int l,int r,int id)
{
    if(l<=L&&r>=R)
    {
        return a[id][i].sum;
    }
    pushdown(i,id);
    int mid=(L+R)/2;ll ans=0;
    if(l<=mid)
        ans+=query(i*2,L,mid,l,r,id);
    if(r>mid)
        ans+=query(i*2+1,mid+1,R,l,r,id);
    return ans;
}
int main()
{
    int n,m,op,x,y,val;
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
        scanf("%d",&b[i]);
    for(int i=0; i<=20; i++)
        build(1,1,n,i);
    scanf("%d",&m);
    while(m--)
    {
        scanf("%d",&op);
        if(op==1)
        {
            scanf("%d%d",&x,&y);
            ll ans=0;
            for(int i=0; i<=20; i++)
                ans+=1ll*(1ll*1<<i)*(1ll*query(1,1,n,x,y,i));
            printf("%lld\n",ans);
        }
        else
        {
            scanf("%d%d%d",&x,&y,&val);
            for(int i=0; i<=20; i++)
            {
                if((val>>i)&1)
                    update(1,1,n,x,y,i);
            }
        }
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值