CF242E XOR on Segment

CF242E XOR on Segment

分析

如果有一定树剖基础的同学,非常建议赶忙练手写一道洛谷树(这是我的题解,里边也附有洛谷树的原题链接)。

这里有一个经验,如果我们需要进行对区间进行位运算,那我们通常会开多个线段树,因为位运算可以分别对每一位来看

但这里有一些需要注意的,如果我们维护的其他值,会发生跳树操作,则这样就不行了,往往要用其他数据结构,或推公式

跳树操作是指,例如我们同时维护区间加与区间异或,则我们的区间加操作的时候,会同时波及到不同位的许多线段树,这就不行了

好啦,有了这个思路,写这个题目,简直轻松哇。

我们来看看两个操作。

区间询问

询问区间内的所有值的和,那我们只需看看对每一位而言,其上1的数量,最后累加起来就好。这里说的有些草率,到时候看代码就明白啦。

区间异或

依然考虑对每一个位,若x的当前位上是1,则代表[l,r]区间的所有数当前位都应当取反(这点很好想,推一下!!)

我们来直接看代码。

Ac_code

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
struct Node
{
    int l,r,cnt,tag;//cnt维护当前位区间内1的个数,tag区间反转标记
}tr[N<<2][21];
int w[N];
int n,m;

void pushup(int u,int k)
{
    tr[u][k].cnt = tr[u<<1][k].cnt + tr[u<<1|1][k].cnt;
}

void build(int u,int l,int r,int k)
{
    tr[u][k] = {l,r};
    if(l==r)
    {
        if(w[l]>>k&1) tr[u][k].cnt = 1;
        return ;
    }
    int mid = l + r >> 1;
    build(u<<1,l,mid,k),build(u<<1|1,mid+1,r,k);
    pushup(u,k);
}

void pushdown(int u,int k)
{
    auto &root = tr[u][k],&left = tr[u<<1][k],&right = tr[u<<1|1][k];
    if(root.tag)
    {
        left.cnt = left.r - left.l + 1 - left.cnt;
        left.tag ^= 1;
        right.cnt = right.r - right.l + 1 - right.cnt;
        right.tag ^= 1;
        root.tag = 0;
    }
}

void modify(int u,int l,int r,int k)
{
    if(l<=tr[u][k].l&&tr[u][k].r<=r)
    {
        tr[u][k].cnt = tr[u][k].r - tr[u][k].l + 1 - tr[u][k].cnt;
        tr[u][k].tag ^= 1;
        return ;
    }
    pushdown(u,k);
    int mid = tr[u][k].l + tr[u][k].r >> 1;
    if(l<=mid) modify(u<<1,l,r,k);
    if(r>mid) modify(u<<1|1,l,r,k);
    pushup(u,k);
} 

int query(int u,int l,int r,int k)
{
    if(l<=tr[u][k].l&&tr[u][k].r<=r) return tr[u][k].cnt;
    pushdown(u,k);
    int mid = tr[u][k].l + tr[u][k].r >> 1;    
    int res = 0;
    if(l<=mid) res += query(u<<1,l,r,k);
    if(r>mid) res += query(u<<1|1,l,r,k);
    return res;
}

int ksm(int a,int b)//计算位权
{
    int res = 1;
    while(b)
    {
        if(b&1) res *= a;
        a*=a;
        b>>=1;
    }
    return res;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",w+i);
    for(int i=0;i<=20;i++)//对每一个线段树初始化
        build(1,1,n,i);
    scanf("%d",&m);
    while(m--)
    {
        int op,l,r,x;
        scanf("%d%d%d",&op,&l,&r);
        if(op==1)
        {
            LL res = 0;
            for(int i=0;i<=20;i++)
            {
                int t = ksm(2,i);//对每一位计算位权
                res += 1ll*query(1,l,r,i)*t;//然后该位1的个数*位权
            }
            printf("%lld\n",res);
        }
        else 
        {
            scanf("%d",&x);
            for(int i=0;i<=20;i++)//若x当前位为1,则区间内所有数,该位取反
                if(x>>i&1)
                    modify(1,l,r,i);
        }
    }
    return 0;
}   
posted @ 2022-05-14 19:42  艾特玖  阅读(59)  评论(0)    收藏  举报