[nowcoder]红球进黑洞

题目

链接:https://ac.nowcoder.com/acm/contest/6046/C

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

题目描述
在心理疏导室中有一种奇特的疏导工具,叫做红球。红球被提前分为了许多正方形小方格。
每当有人来找ATB做心理疏导时,ATB就会让他去先玩红球,然后通过红球小格方的高度来判断一个人的压力程度的高低
具体地讲,ATB会让该人对于一个序列执行以下操作

  1. 区间求和,即输入l,r,输出 ∑ i = l r a i \sum_{i=l}^{r}a_i i=lrai
  2. 区间异或,即输入l,r,k,对于 l ≤ i ≤ r l ≤ i ≤ r lir,将 x i x_i xi变为 x i ⊕ k x_i⊕k xik
    可是ATB天天算计那么多答案,已经对这份工作产生了厌烦,所以请你帮帮他,对于一组给定的数据,输出对应的答案
    ATB会将你感谢到爆

输入描述:

第一行两个整数n和m,表示数列长度和询问次数
第二行有 n n n个整数,表示这个数列的初始数值
接下来有 m m m行,形如 1 l r 或者 2 l r k
分别表示查询 ∑ i = l r a i \sum_{i=l}^{r}a_i i=lrai
或者对于 l ≤ i ≤ r l ≤ i ≤ r lir,将 x i x_i xi变为 x i ⊕ k x_i⊕k xik

输出描述:

对于每一个查询操作,输出查询的结果并换行

输入

10 10
8 5 8 9 3 9 8 3 3 6
2 1 4 1
1 2 6
2 9 10 8
1 1 7
2 4 7 8
2 8 8 6
2 2 3 0
1 1 2
2 9 10 4
1 2 3

输出

33
50
13
13

备注:

  1. 数据范围
    对于30%30%30%的数据,保证 n, m, k≤ 10
    对于另外30%30%30%的数据,保证 n, m ≤ 50000, k ∈ {0, 1}
    对于全部100%100%100%的数据,保证 1 ≤ n,m ≤ 105, 0≤ ai,k ≤ 105
  2. 说明
    a⊕b 表示 a xor b

题解:
将数字拆成20个线段树
这样就变成计算区间异或1,和求区间1的个数
码力题

#include
#define ll long long 
using namespace std;
const int S = 21;
const int MAXN = 1e5 + 4;
struct node{
    int tag;
    int v;
}tr[MAXN*4][S];
int n,m;
int o[MAXN];
void update(int k){
    for(int i=0;i<S;i++){
        tr[k][i].v = tr[k<<1][i].v + tr[k<<1|1][i].v;
    }
}
void build(int k,int l,int r){
    if(l==r){
        int vn = o[l];
        for(int i=0;i<S;i++){
            tr[k][i].v = vn % 2;
            vn /= 2;
        }
        return ;
    }
    int mid=(l+r)/2;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    update(k);
}

void changeI(int k,int v,int sum){
    for(int i=0;i<S;i++){
        if(v%2==1){
            tr[k][i].v=sum-tr[k][i].v;
        }
        tr[k][i].tag ^= v % 2;
        v /= 2;
    }
}
void push(int k,int i,int sum){
    tr[k][i].tag = 0;
    tr[k<<1][i].tag ^= 1;
    tr[k<<1|1][i].tag ^= 1;
    int sl=sum-sum/2;
    int sr=sum/2;
    tr[k<<1][i].v=sl - tr[k<<1][i].v;
    tr[k<<1|1][i].v = sr - tr[k<<1|1][i].v; 
}
void change(int k,int l,int r,int a,int b,int v){
    if(l==a && r==b){
        changeI(k,v,r-l+1);
        return ;
    }
    for(int i=0;i<S;i++)if(tr[k][i].tag)push(k,i,r-l+1);
    int mid=(l+r)/2;
    if(b <= mid){
        change(k<<1,l,mid,a,b,v);
    } else if (a > mid){
        change(k<<1|1,mid+1,r,a,b,v);
    } else {
        change(k<<1,l,mid,a,mid,v);
        change(k<<1|1,mid+1,r,mid+1,b,v);
    }
    update(k);
}

ll calc(int k){
    ll res = 0 , bs = 1;
    for(int i=0;i<S;i++){
        res += bs * tr[k][i].v;
        bs *= 2;
    }
    return res;
}

ll ask(int k,int l,int r,int a,int b){
    if(l==a && r==b){
        return calc(k);
    }
    for(int i=0;i<S;i++)if(tr[k][i].tag)push(k,i,r-l+1);
    int mid=(l+r)/2;
    if(b<=mid){
        return ask(k<<1,l,mid,a,b);
    }else if(a > mid){
        return ask(k<<1|1,mid+1,r,a,b);
    }else{
        return ask(k<<1,l,mid,a,mid) + ask(k<<1|1,mid+1,r,mid+1,b);
    }
}

int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&o[i]);
    build(1,1,n);
    while(m--){
        int t,l,r;
        scanf("%d%d%d",&t,&l,&r);
        if(t == 1)printf("%lld\n",ask(1,1,n,l,r));
        if(t == 2){
            int k;scanf("%d",&k);
            change(1,1,n,l,r,k);
        }
    }
    return 0;
}

你可能感兴趣的:(-----------数据结构,线段树)