FZU 2105 Digits Count(按位维护线段树)

【题目链接】 http://acm.fzu.edu.cn/problem.php?pid=2105

【题目大意】

  给出一个序列,数字均小于16,为正数,每次区间操作可以使得
    1. [l,r]区间and一个数
    2. [l,r]区间or一个数
    3. [l,r]区间xor一个数
    4. [l,r]区间查询和
  操作数均为小于16的非负整数

【题解】

  由于操作数很小,因此我们可以按位维护四棵线段树,表示二进制中的第i位,
  对于and操作,只有当and的当前位为0时才对区间有影响,效果是将区间全部变为0,
  对于or和xor操作,当前位为1的时候才对区间有影响,
  因为and和or是全区间变化为同一个值,因此区间和会变为全区间或者0,
  那么and和or的变化只需要看父区间的值来决定子区间的变化而不需要标记传递
  对于xor操作,相当于区间01翻转,用一个标记记录翻转次数即可,

【代码】

#include <cstdio>
#include <algorithm>
using namespace std;
const int N=1000010;
int T[4][N<<2],tag[4][N<<2],a[N];
void up(int k,int x){T[k][x]=T[k][x<<1]+T[k][x<<1|1];}
void pb(int k,int x,int l,int r){
    int mid=(l+r)>>1;
    if(T[k][x]==r-l+1){T[k][x<<1]=mid-l+1;T[k][x<<1|1]=r-mid;}
    if(!T[k][x]){T[k][x<<1]=T[k][x<<1|1]=0;}
    if(tag[k][x]){
        tag[k][x]^=1;
        if(T[k][x]!=r-l+1&&T[k][x]){
            T[k][x<<1]=mid-l+1-T[k][x<<1];
            T[k][x<<1|1]=r-mid-T[k][x<<1|1];
        }tag[k][x<<1]^=1;tag[k][x<<1|1]^=1;
    }
}
void build(int k,int x,int l,int r){
    int mid=(l+r)>>1;
    tag[k][x]=0;
    if(l==r){T[k][x]=(a[l]>>k)&1;return;}
    build(k,x<<1,l,mid);build(k,x<<1|1,mid+1,r);
    up(k,x);
}
void update(int k,int x,int l,int r,int L,int R,char op){
    int mid=(l+r)>>1;
    if(L<=l&&r<=R){
        if(op==‘A‘)T[k][x]=0;
        if(op==‘O‘)T[k][x]=r-l+1;
        if(op==‘X‘)T[k][x]=(r-l+1)-T[k][x],tag[k][x]^=1;
        return;
    }pb(k,x,l,r);
    if(L<=mid)update(k,x<<1,l,mid,L,R,op);
    if(mid+1<=R)update(k,x<<1|1,mid+1,r,L,R,op);
    up(k,x);
}
int query(int k,int x,int l,int r,int L,int R){
    int mid=(l+r)>>1;
    if(L<=l&&r<=R)return T[k][x]; pb(k,x,l,r);
    int res=0;
    if(L<=mid)res+=query(k,x<<1,l,mid,L,R);
    if(mid+1<=R)res+=query(k,x<<1|1,mid+1,r,L,R);
    return res;
}
int Cas,n,m;
char op[10];
int main(){
    scanf("%d",&Cas);
    while(Cas--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        for(int i=0;i<4;i++)build(i,1,1,n);
        while(m--){
            scanf("%s",op);
            if(op[0]==‘S‘){
                int l,r,ans=0;
                scanf("%d%d",&l,&r);
                l++;r++;
                for(int i=0;i<4;i++)ans+=query(i,1,1,n,l,r)*(1<<i);
                printf("%d\n",ans);
            }else{
                int l,r,x;
                scanf("%d%d%d",&x,&l,&r); l++,r++;
                for(int i=0;i<4;i++)if((op[0]==‘A‘)^((x>>i)&1))update(i,1,1,n,l,r,op[0]);
            }
        }
    }return 0;
}
时间: 2024-11-08 09:42:15

FZU 2105 Digits Count(按位维护线段树)的相关文章

ACM: FZU 2105 Digits Count - 位运算的线段树【黑科技福利】

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 integer

fzu 2105 Digits Count 线段树

题目链接:http://acm.fzu.edu.cn/problem.php?pid=2105 题意: 给出一个数组A[0]-A[n-1],每个数最大是16.有4种操作: AND opn L R:L-R区间内的数都AND上opn这个数 OR opn L R:L-R区间内的数都OR上opn这个数 XOR opn L R:L-R区间内的数都XOR上opn这个数 SUM L R:求L-R区间内所有数的和. 0 <= opn <= 16 思路: 可以发现A[i]和opn最大都不超过16,所以可以设4个

FZU 2105 Digits Count(位数计算)

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

POJ 2777 Count Color(位运算+线段树+lazy+区间更新)

Count Color Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 39905   Accepted: 12034 Description Chosen Problem Solving and Program design as an optional course, you are required to solve all kinds of problems. Here, we get a new problem.

FZU2105 Digits Count(按位建线段树)题解

题意: 给出区间与.或.异或\(x\)操作,还有询问区间和. 思路: 因为数比较小,我们给每一位建线段树,这样每次只要更新对应位的答案. 与\(0\)和或\(1\)相当于重置区间,异或\(1\)相当于翻转区间,那么设出两个\(lazy\)搞一下.注意父区间\(pushdown\)重置标记时,子区间的翻转标记要清空. 代码: #include <map> #include <set> #include <queue> #include <cmath> #inc

FOJ 2105 Digits Count

题意:对一串数字进行抑或某数,和某数,或某数,统计某区间和的操作. 思路:因为化成二进制就4位可以建4颗线段树,每颗代表一位二进制. and 如果该为是1  直接无视,是0则成段赋值为0: or  如果是0 无视,是1则成段赋值为1: xor 成段亦或,1个数和0个数交换: sum 求和: #include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include <

ZOJ 1610 Count the Colors (线段树区间更新)

题目链接 题意 : 一根木棍,长8000,然后分别在不同的区间涂上不同的颜色,问你最后能够看到多少颜色,然后每个颜色有多少段,颜色大小从头到尾输出. 思路 :线段树区间更新一下,然后标记一下,最后从头输出. //ZOJ 1610 #include <cstdio> #include <cstring> #include <iostream> using namespace std ; int p[8010*4],lz[8010*4] ,hashh[8010*4],has

ZOJ1610 Count the Colors 经典线段树染色问题

题意,给你n个  x,y,c,意思就是区间[x,y]被染成C色,但是颜色会被覆盖的,染色操作完成以后 问你每种颜色有多少段 并输出颜色编号id跟段数cnt 经典问题,不过写的有点撮吧,没去看别人的,这个方法应该是最传统的最普通的,常规的开数组记录,也许大神们有更高端的方法 #include<iostream> #include<cstdio> #include<list> #include<algorithm> #include<cstring>

非结构体线段树版 ZJU 1610 Count the Colors (线段树区间更新)

Painting some colored segments on a line, some previously painted segments may be covered by some the subsequent ones. Your task is counting the segments of different colors you can see at last. Input The first line of each data set contains exactly