牛客练习赛29E位运算?位运算!(位运算线段树>>,<<,|,&)

链接:https://ac.nowcoder.com/acm/contest/211/E?&headNav=www
来源:牛客网

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

题目描述

请实现一个数据结构支持以下操作:
区间循环左右移,区间与,区间或,区间求和。

输入描述:

第一行n,q表示数列长度及操作次数。
第二行n个数表示初始序列。
接下来q行表示操作。
操作格式如下:
一行表示一个操作。所有操作形如 opt l r v。
opt=1 表示将区间[l,r]循环右移v位。
opt=2 表示将区间[l,r]循环左移v位。
opt=3 表示将区间[l,r]按位或上v。
opt=4 表示将区间[l,r]按位与上v。
opt=5 询问区间[l,r]的和。
保证opt=1或2时 1 ≤ v ≤ 20
注意:为了优化你的做题体验,操作5也会输入一个v,但是是没有意义的。
注意:循环左右移在20个二进制位的意义下进行

输出描述:

对于每个opt=5的操作,输出一个数表示答案

示例1

输入

10 10
10112 23536 1305 7072 12730 29518 12315 3459 12435 29055
4 5 10 12373
2 1 6 7
5 4 10 24895
1 1 4 8
5 3 7 7767
5 7 9 6127
4 2 8 30971
5 4 10 2663
1 7 10 1
1 2 9 5

输出

2001530
1600111
24611
49482

备注:

1 ≤ N,Q ≤ 2*10 5 0 ≤ a i < 2 20
一些说明:
1. 对于00000000000000000101,右移一位后会变成10000000000000000010
2. 不是区间位移,是区间中的每一个数的二进制位的位移
 
和FZU2105有类似之处,只不过把位运算换成了异或运算,之前写过一篇题解: https://blog.csdn.net/qq_43906000/article/details/102422600
 
很显然,或运算和与运算在二进制的状态表示下只是区间覆盖而已,所以一个标记就够了,&0的任何数都是0,|1的任何数都是1,其他的不变。
接下来就是位运算的操作了,这个位运算有点特别,我们可以直接模拟就好了,然后打上标记,由于二进制的位数是确定的就,所以左移(<<)x位,相当于右移20-x位。反之亦然。不过有点奇怪的是
tree[i]=p[(i+val)%nb]
 这种写法应该是代表左移的,那么也就是说右移的应该是20-x位,但这么写就不对了。。。
 
接下来需要注意的是位移的同时,不仅要把二进制下的数移走, 同时还要移走懒标记!!!这个WA了n发。。。
 
以下是pushdown的代码,也是核心代码:
void pushdown(int l,int r,int rt)
{
    int mid=(l+r)>>1;
    if(tree[rt].f1){//位运算
        int p[21],ps[21],p1[21],ps1[21];
        for (int i=0; i<nb; i++) {
            p[i]=tree[ls].sum[i];p1[i]=tree[ls].f2[i];
            ps[i]=tree[rs].sum[i];ps1[i]=tree[rs].f2[i];
        }
        tree[ls].f1+=tree[rt].f1;
        tree[rs].f1+=tree[rt].f1;
        for (int i=0; i<nb; i++){
            tree[ls].sum[i]=p[(i+tree[rt].f1)%nb];
            tree[rs].sum[i]=ps[(i+tree[rt].f1)%nb];
            tree[ls].f2[i]=p1[(i+tree[rt].f1)%nb];
            tree[rs].f2[i]=ps1[(i+tree[rt].f1)%nb];
        } 
        tree[rt].f1=0;
    }
    for (int i=0; i<nb; i++){
        if (tree[rt].f2[i]!=-1) {//区间覆盖的标记传递
            tree[ls].sum[i]=(mid-l+1)*tree[rt].f2[i];
            tree[rs].sum[i]=(r-mid)*tree[rt].f2[i];
            tree[ls].f2[i]=tree[rt].f2[i];
            tree[rs].f2[i]=tree[rt].f2[i];
            tree[rt].f2[i]=-1;
        }
    }
}
接下来询问操作的时候如果还像我之前FZU2105那么写的话就会T(我就是这样T了n发的)。。。所以也需要注意拆位的时候尽量在函数里面拆位,不要每拆一位就跑一遍函数。由于加了快读所以跑起来挺快的,1400ms+
 
以下是AC代码:
#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define ls rt<<1
#define rs rt<<1|1

const int mac=2e5+10;
const int nb=20;

struct Tree
{
    int sum[21],f1,f2[21];//f1代表位运算,f2代表或与运算 
}tree[mac<<2];
int a[mac];

void build(int l,int r,int rt)
{
    for (int i=0; i<nb; i++) tree[rt].f2[i]=-1;
    if (l==r){
        for (int i=0; i<nb; i++)
            if (a[l]&(1<<i)) tree[rt].sum[i]++;
        return;    
    }
    int mid=(l+r)>>1;
    build(lson);build(rson);
    for (int i=0; i<nb; i++) 
        tree[rt].sum[i]=tree[ls].sum[i]+tree[rs].sum[i];
}

void pushdown(int l,int r,int rt)
{
    int mid=(l+r)>>1;
    if(tree[rt].f1){
        int p[21],ps[21],p1[21],ps1[21];
        for (int i=0; i<nb; i++) {
            p[i]=tree[ls].sum[i];p1[i]=tree[ls].f2[i];
            ps[i]=tree[rs].sum[i];ps1[i]=tree[rs].f2[i];
        }
        tree[ls].f1+=tree[rt].f1;
        tree[rs].f1+=tree[rt].f1;
        for (int i=0; i<nb; i++){
            tree[ls].sum[i]=p[(i+tree[rt].f1)%nb];
            tree[rs].sum[i]=ps[(i+tree[rt].f1)%nb];
            tree[ls].f2[i]=p1[(i+tree[rt].f1)%nb];
            tree[rs].f2[i]=ps1[(i+tree[rt].f1)%nb];
        } 
        tree[rt].f1=0;
    }
    for (int i=0; i<nb; i++){
        if (tree[rt].f2[i]!=-1) {
            tree[ls].sum[i]=(mid-l+1)*tree[rt].f2[i];
            tree[rs].sum[i]=(r-mid)*tree[rt].f2[i];
            tree[ls].f2[i]=tree[rt].f2[i];
            tree[rs].f2[i]=tree[rt].f2[i];
            tree[rt].f2[i]=-1;
        }
    }
}

void update(int l,int r,int rt,int L,int R,int val,int id)
{
    if (l>=L && r<=R){
        if (id==1){
            int p[21],p1[21];
            for (int i=0; i<nb; i++) 
                p[i]=tree[rt].sum[i],p1[i]=tree[rt].f2[i];
            for (int i=0; i<nb; i++) 
                tree[rt].sum[i]=p[(i+val)%nb],tree[rt].f2[i]=p1[(i+val)%nb];
            tree[rt].f1+=val;
        }
        else if (id==2) 
            for (int i=0; i<nb; i++) 
                {if (val&(1<<i)) tree[rt].sum[i]=(r-l+1),tree[rt].f2[i]=1;}
        else 
            for (int i=0; i<nb; i++)
                {if (!(val&(1<<i))) tree[rt].sum[i]=0,tree[rt].f2[i]=0;}
        return;
    }
    pushdown(l,r,rt);
    int mid=(l+r)>>1;
    if (mid>=L) update(lson,L,R,val,id);
    if (mid<R) update(rson,L,R,val,id);
    for (int i=0; i<nb; i++) 
        tree[rt].sum[i]=tree[ls].sum[i]+tree[rs].sum[i];
}

ll query(int l,int r,int rt,int L,int R)
{
    ll ans=0;
    if (l>=L && r<=R){
        for (int i=0; i<nb; i++)
            ans+=tree[rt].sum[i]*(1ll<<i);
        return ans;
    } 
    pushdown(l,r,rt);
    int mid=(l+r)>>1;
    if (mid>=L) ans+=query(lson,L,R);
    if (mid<R) ans+=query(rson,L,R);
    return ans; 
}

void in(int &x)
{
    int f=0;
    char ch=getchar();
    while (ch>'9' || ch<'0') ch=getchar();
    while (ch>='0' && ch<='9') f=(f<<1)+(f<<3)+ch-'0',ch=getchar();
    x=f;
}

void print(ll x)
{
    if (x>=10)
        print(x/10);
    putchar(x%10+'0');
}

int main()
{int n,q;
    in(n);in(q);
    for (int i=1; i<=n; i++) in(a[i]);
    build(1,n,1);
    while (q--){
        int opt,l,r,val;
        in(opt);in(l);in(r);in(val);
        if(opt==1) update(1,n,1,l,r,val,1);//>>
        else if (opt==2) update(1,n,1,l,r,nb-val,1);//<<
        else if (opt==3) update(1,n,1,l,r,val,2);
        else if (opt==4) update(1,n,1,l,r,val,3);
        else {
            ll ans=query(1,n,1,l,r);
            print(ans);
            putchar('\n');
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/lonely-wind-/p/11749281.html