线段树-位运算

 FZU 2105  Digits Count

Time Limit:10000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u

Practice

Description

Given N integers A={A[0],A[1],...,A[N-1]}. Here we have some operations:

Operation 1: AND opn L R

Here opn, L and R are integers.

For L≤i≤R, we do A[i]=A[i] AND opn (here "AND" is bitwise operation).

Operation 2: OR opn L R

Here opn, L and R are integers.

For L≤i≤R, we do A[i]=A[i] OR opn (here "OR" is bitwise operation).

Operation 3: XOR opn L R

Here opn, L and R are integers.

For L≤i≤R, we do A[i]=A[i] XOR opn (here "XOR" is bitwise operation).

Operation 4: SUM L R

We want to know the result of A[L]+A[L+1]+...+A[R].

Now can you solve this easy problem?

Input

The first line of the input contains an integer T, indicating the number of test cases. (T≤100)

Then T cases, for any case, the first line has two integers n and m (1≤n≤1,000,000, 1≤m≤100,000), indicating the number of elements in A and the number of operations.

Then one line follows n integers A[0], A[1], ..., A[n-1] (0≤A[i]<16,0≤i<n).

Then m lines, each line must be one of the 4 operations above. (0≤opn≤15)

Output

For each test case and for each "SUM" operation, please output the result with a single line.

Sample Input

1
4 4
1 2 4 7
SUM 0 2
XOR 5 0 0
OR 6 0 3
SUM 0 2

Sample Output

7
18

Hint

A = [1 2 4 7]

SUM 0 2, result=1+2+4=7;

XOR 5 0 0, A=[4 2 4 7];

OR 6 0 3, A=[6 6 6 7];

SUM 0 2, result=6+6+6=18.

ac:

#include"algorithm"
#include"iostream"
#include"cstring"
#include"cstdlib"
#include"cstdio"
#include"string"
#include"vector"
#include"stack"
#include"queue"
#include"cmath"
#include"map"
using namespace std;
typedef long long LL ;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1   
#define FK(x) cout<<"["<<x<<"]\n"
#define memset(x,y) memset(x,y,sizeof(x))
#define memcpy(x,y) memcpy(x,y,sizeof(x))
#define smallfor(T)  for(int i=0 ;i<T ;i++)
#define bigfor(T)  for(int qq=1;qq<= T ;qq++)
 
const int MX =1111111;
const int INF=0x3f3f3f3f;
int sum[MX<<2][4],lazy[MX<<2][4];
void PushUp(int rt,int i) {
    sum[rt][i]=sum[rt<<1][i]+sum[rt<<1|1][i];
}
 
void PushDown(int rt,int m,int i) {
    if(lazy[rt][i]==0) {     //如果进行了AND操作,并且该位为0 清空下面子树。
        lazy[rt<<1][i]=0;
        lazy[rt<<1|1][i]=0;
        sum[rt<<1][i]=sum[rt<<1|1][i]=0;
    }
    if(lazy[rt][i]==1) {     //如果进行了OR 操作,并且该位为1 填满下面子树。
        lazy[rt<<1][i]=1;
        lazy[rt<<1|1][i]=1;
        sum[rt<<1][i]=m-(m>>1);
        sum[rt<<1|1][i]=m>>1;
    }
    if(lazy[rt][i]==2) {     //如果进行了XOR操作
        if(lazy[rt<<1][i]==INF) { //如果没有进行过任何操作,标记为XOR操作
            lazy[rt<<1][i]=2;
            sum[rt<<1][i]=m-(m>>1)-sum[rt<<1][i];
        } else if(lazy[rt<<1][i]==2) {  //如果进行过XOR操作,a^b^b==a 恢复操作内容。
            lazy[rt<<1][i]=INF;
            sum[rt<<1][i]=m-(m>>1)-sum[rt<<1][i];
        } else {                  //如果进行了操作并且不是XOR操作 将该操作再取XOR操作
            lazy[rt<<1][i]^=1;
            if(lazy[rt<<1][i]==0) sum[rt<<1][i]=0;
            else  sum[rt<<1][i]=m-(m>>1);
        }                        
//                           另一棵子树用同样的方法处理
                 
        if(lazy[rt<<1|1][i]==INF) {
            lazy[rt<<1|1][i]=2;
            sum[rt<<1|1][i]=(m>>1)-sum[rt<<1|1][i];
        } else if(lazy[rt<<1|1][i]==2) {
            lazy[rt<<1|1][i]=INF;
            sum[rt<<1|1][i]=(m>>1)-sum[rt<<1|1][i];
        } else {
            lazy[rt<<1|1][i]^=1;
            if(lazy[rt<<1|1][i]==0) sum[rt<<1|1][i]=0;
            else sum[rt<<1|1][i]=(m>>1);
        }
    }
    lazy[rt][i]=INF; //标记lazy为空
}
 
void Build(int l,int r,int rt) {
    for(int i=0; i<4; i++) lazy[rt][i]=INF; //清空懒惰标记
    if(r==l) {
        int temp;
        scanf("%d",&temp);
//      FK("temp=="<<temp);
        for(int i=0; i<4; i++) {
            sum[rt][i]=(bool)(temp&(1<<i));//【这里一定要取(bool)否则得到的值不会是1,而是比 1大的数】
//          该题目的方法是用sum保存每个位上值的总数,再改变为10进制,求和。
//          把数按照二进制保存在4个位上面
//          FK(sum[rt][i]);
        }
        return;
    }
    int m=(r+l)>>1;
    Build(lson);
    Build(rson);
    for(int i=0; i<4; i++) PushUp(rt,i);
}
 
void UpData(int L,int R,int v,int i,int l,int r,int rt) {
    if(r<=R&&L<=l) {
        switch(v) {
            case 0:
                sum[rt][i]=0,lazy[rt][i]=v;
                //如果是进行AND操作,并且是0,清空和。
                break;
            case 1:
                sum[rt][i]=r-l+1,lazy[rt][i]=v;
                //如果是进行OR 操作,并且是1,填满和。
                break;
            case 2:
                sum[rt][i]=r-l+1-sum[rt][i];
                if(lazy[rt][i]==2) lazy[rt][i]=INF;
                else if(lazy[rt][i]==INF) lazy[rt][i]=2;
                else lazy[rt][i]^=1;
                break;
            default:
                break;
        }
        return ;
    }
    PushDown(rt,r-l+1,i);
    int m=(r+l)>>1;
    if(L<=m)UpData(L,R,v,i,lson);
    if(R>m) UpData(L,R,v,i,rson);
    PushUp(rt,i);
}
 
int Query(int L,int R,int i,int l,int r,int rt) {
    if(L<=l&&r<=R) {
        return sum[rt][i];
//      返回这个数该位的和。
    }
    int m=(r+l)>>1;
    int sum=0;
    PushDown(rt,r-l+1,i);
    if(L<=m)sum+=Query(L,R,i,lson);
    if(R>m) sum+=Query(L,R,i,rson);
    return sum;
}
 
int main() {
    int T;
    scanf("%d",&T);
    bigfor(T) {
        int n,m;
        scanf("%d%d",&n,&m);
        char op[5];
        Build(0,n-1,1);
//      FK("Build Success!");
        for(int i=0; i<m; i++) {
            scanf("%s",op);
            if(op[0]=='S') {
                int l,r;
                int ans=0;
                scanf("%d%d",&l,&r);
                for(int j=0; j<4; j++) ans+=Query(l,r,j,0,n-1,1)<<j;
//              将每一位的数字和用10进制进位后相加。
                printf("%d\n",ans);
            } else {
                int opn,l,r;
                char v;
                scanf("%d%d%d",&opn,&l,&r);
                if(op[0]=='A') {  //AND为&如果某位上为 1 那么值不变 否则全变为0;【区间覆盖】
                    for(int j=0; j<4; j++) {
                        int x=opn&(1<<j);
//                      FK("j=="<<j<<"  x=="<<x);
                        if(!x)UpData(l,r,0,j,0,n-1,1);
                    }
                }
                if(op[0]=='O') {  //OR 为|如果某位上为 0 那么值不变 否则全变为1;【区间覆盖】
                    for(int j=0; j<4; j++) {
                        int x=opn&(1<<j);
//                      FK("j=="<<j<<"  x=="<<x);
                        if(x)UpData(l,r,1,j,0,n-1,1);
                    }
                }
                if(op[0]=='X') {  //XOR为^如果某位上为 0 那么值不变 否则0->1,1->0【区间更新】
                    for(int j=0; j<4; j++) {
                        int x=opn&(1<<j);
//                      FK("j=="<<j<<"  x=="<<x);
                        if(x)UpData(l,r,2,j,0,n-1,1);
                    }
                }
            }
        }
    }
    return 0;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值