Codeforces 242E. XOR on Segment

题目大意:

给出一个序列,有两种操作,一种是计算l到r的和,另一种是让l到r的数全部和x做异或运算。

做法:

很显然直接暴力是不可能的(但是这题刚刚出来的时候,很多人用暴力水过去了,后来加强的数据吧),又是两种操作,又想到了线段树。。但是这并不简单,异或操作该怎么处理?

异或是一种位运算,如果x的第j位是1,那么说明l到r的每个数的第j位都要反转,(0^1=1,1^1=0),如果是0,那么不变。既然是位运算,那么可不可以将每一位作为线段树单独维护呢?

好像可以呢!异或操作的话,相当于是一种区间操作,只需要将l到r的某些位进行反转操作不就行了吗?反转操作什么的,打上lazy tag不就OK啦,求和操作也可以计算出l到r的每一位上有多少个1来算出最后的和。

好,那么理一下思路,首先确定建立多少个线段树,数的范围是10^6,也就是说,我们最多只需要建立20个线段树即可,再写好线段树的bulid,update,query三个函数。每个节点上需要维护两个值,一个是该区间有多少个1(用于求和),一个是该区间是否反转(lazy tag)。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#define N 100010
#define L(x) (x)<<1
#define R(x) (x)<<1|1
using namespace std;
struct node
{
    int l,r,cnt,lazy;
    node(long long ll,long long rr,long long c,long long la)
    {
        l=ll,r=rr,cnt=c,lazy=la;
    }
    node()
    {
        l=r=cnt=lazy=0;
    }
}p[20][N*4];
long long a[N][20],num[N],two[22];
long long bulid(long long id,long long l,long long r,long long na)
{
    if(l==r){p[na][id]=node(l,r,a[l][na],0);return a[l][na];}
    long long mid=(l+r)/2;
    long long cur=bulid(L(id),l,mid,na)+bulid(R(id),mid+1,r,na);
    p[na][id]=node(l,r,cur,0);
    return cur;
}
void revers(long long id,long long l,long long r,long long na)
{
    if(p[na][id].l==l && p[na][id].r==r)
    {
        p[na][id].lazy=1-p[na][id].lazy;
        p[na][id].cnt=p[na][id].r-p[na][id].l+1-p[na][id].cnt;
        return ;
    }
    if(p[na][id].lazy)//lazy tag 下放
    {
        p[na][id].lazy=0;
        p[na][L(id)].lazy=1-p[na][L(id)].lazy;
        p[na][R(id)].lazy=1-p[na][R(id)].lazy;
        p[na][L(id)].cnt=p[na][L(id)].r-p[na][L(id)].l+1-p[na][L(id)].cnt;
        p[na][R(id)].cnt=p[na][R(id)].r-p[na][R(id)].l+1-p[na][R(id)].cnt;
    }
    long long mid=(p[na][id].l+p[na][id].r)/2;
    if(mid<l) revers(R(id),l,r,na);
    else if(mid>=r) revers(L(id),l,r,na);
    else revers(L(id),l,mid,na),revers(R(id),mid+1,r,na);
    p[na][id].cnt=p[na][L(id)].cnt+p[na][R(id)].cnt;
}
long long query(long long id,long long l,long long r,long long na)
{
    if(p[na][id].l==l && p[na][id].r==r) return p[na][id].cnt;
    if(p[na][id].lazy)
    {
        p[na][id].lazy=0;
        p[na][L(id)].lazy=1-p[na][L(id)].lazy;
        p[na][R(id)].lazy=1-p[na][R(id)].lazy;
        p[na][L(id)].cnt=p[na][L(id)].r-p[na][L(id)].l+1-p[na][L(id)].cnt;
        p[na][R(id)].cnt=p[na][R(id)].r-p[na][R(id)].l+1-p[na][R(id)].cnt;
    }
    long long mid=(p[na][id].l+p[na][id].r)/2;
    if(mid>=r) return query(L(id),l,r,na);
    else if(mid<l) return query(R(id),l,r,na);
    else return query(L(id),l,mid,na)+query(R(id),mid+1,r,na);
}
int main()
{
    two[0]=1;
    for(long long i=1;i<21;i++) two[i]=two[i-1]*2;
    long long n;
    scanf("%I64d",&n);
    for(long long i=1;i<=n;i++)
    {
        scanf("%I64d",&num[i]);
        long long j=0;
        while(num[i]>0)
        {
            a[i][j++]=num[i]&1;
            num[i]>>=1;
        }
    }
    for(long long i=0;i<20;i++)
        bulid(1,1,n,i);
    long long m;
    scanf("%I64d",&m);
    while(m--)
    {
        long long ty;
        scanf("%I64d",&ty);
        if(ty==1)
        {
            long long a,b;
            scanf("%I64d%I64d",&a,&b);
            long long cur=0;
            for(long long i=0;i<20;i++)
                cur+=query(1,a,b,i)*two[i];
            cout<<cur<<endl;
        }
        else
        {
            long long a,b,x;
            scanf("%I64d%I64d%I64d",&a,&b,&x);
            for(long long i=0;i<20 && x;i++,x>>=1)
            {
                if(x&1) revers(1,a,b,i);
            }
        }
    }
    return 0;
}
时间: 2024-10-03 14:57:50

Codeforces 242E. XOR on Segment的相关文章

codeforces 242E. XOR on Segment 线段树

题目链接 给n个数, 两种操作, 一种是求区间内的数的和, 一种是将区间内的数异或x. 异或x没有什么思路, 单个异或肯定超时, 区间异或也没有办法做....后来才知道可以按位建线段树, 这样建20棵线段树就可以. 每一次异或, 对于给定的x, 如果x的第i位是1, 那么就将第i棵线段树在给定的区间内0,1翻转, 这是很基础的操作. 对于区间求和操作, 我们可以求出给定的区间, 从高位到低位, 每一位依次有多少个1, 然后就可以直接求出来, 感觉不好表达....具体看代码. 1 #include

Codeforces 242E. XOR on Segment (二维线段树 lazy操作 xor)

题目链接: http://codeforces.com/problemset/problem/242/E 题意: 给出一个序列,有两种操作,一种是计算l到r的和,另一种是让l到r的数全部和x做异或运算. 思路: from: http://blog.csdn.net/u013912596/article/details/39006317 很显然直接暴力是不可能的,又是两种操作,又想到了线段树..但是这并不简单,异或操作该怎么处理? 异或是一种位运算,如果x的第j位是1,那么说明l到r的每个数的第j

Codeforces Round #149 (Div. 2) E. XOR on Segment (线段树成段更新+二进制)

题目链接:http://codeforces.com/problemset/problem/242/E 给你n个数,m个操作,操作1是查询l到r之间的和,操作2是将l到r之间的每个数xor与x. 这题是线段树成段更新,但是不能直接更新,不然只能一个数一个数更新.这样只能把每个数存到一个数组中,长度大概是20吧,然后模拟二进制的位操作.仔细一点就行了. 1 #include <iostream> 2 #include <cstdio> 3 #include <cmath>

Codeforces 627A XOR Equation(思路)

题目大概说两个正整数a.b,已知s=a+b以及x=a xor b的值,问有几种a.b这样的数对. 我知道异或相当于无进位的加法,s-x就是其各个位置的进位,比如s-x=1010,那就表示a和b的第1位和第3位发生的进位. 这样,对于某些位其值就能确定,对于有些位其值不能确定(该位xor和为1且没有发生进位),这时a和b的该位都能选择0或者1,对于不确定的就是乘法原理答案累乘2. 另外还有一些情况是不可能的,首先s<x不可能,s-x是奇数不可能,某一位xor和是1且发生了进位这不可能. 最后注意是

CodeForces - 617E XOR and Favorite Number (莫队+前缀和)

Bob has a favorite number k and ai of length n. Now he asks you to answer m queries. Each query is given by a pair li and ri and asks you to count the number of pairs of integers i and j, such that l ≤ i ≤ j ≤ r and the xor of the numbers ai, ai + 1,

codeforces 1285E. Delete a Segment

链接:https://codeforces.com/problemset/problem/1285/E 题意:给一个数轴上有n个线段集,线段集若有相交,则合并为一个新的合并线段集,比如[1,6]和[2,9],因为两个线段有相交,所以要合并为[1,9],先问删掉给定的n个线段集中的任意一个,剩下的n-1个线段组成的新的合并线段集数量最大是多少? 思路: 这道题首先想到的是并查集做法,枚举删除任意一条线段后,剩下的线段组成的集合是多少,取max,这个复杂度有n2 × 并查集复杂度,显然是不行的.那么

(莫队算法)CodeForces - 617E XOR and Favorite Number

题意: 长度为n的数列,m次询问,还有一个k.每次询问询问询问从数列的L到R内有多少个连续子序列异或起来等于k. 分析: 因为事先知道这题可以用莫队写,就正好用这题练习莫队. 预处理每个前缀异或和. 然后莫队按分块排序后,不断更新,用一个数组cnt[]记录当前L到R前缀和的数量. R向右拉,新增的数量就是cnt[pre^k],pre表示当前这个R位置的前缀异或和,然后更新一下cnt. 其他的也类似. 算是一个比较好的入门题. 代码: 1 #include <cstdio> 2 #include

CodeForces 617E XOR and Favorite Number

莫队算法. #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> using namespace std; const int maxn=100000+10; int a[maxn],pre[maxn]; long long cnt[20*maxn]; int pos[maxn]; int n,m,k; long long ans[maxn]; long long Ans

CodeForces 635C XOR Equation

位运算. 又涨姿势了:$a + b = (aXORb) + 2*(aANDb)$,$ (aXORb)$是不进位的部分,$2*(aANDb)$为进位之后的部分,相加就是$a + b$. 知道了这个转换,这题就很容易了.设$n=a+b$,$m=(aXORb)$,$x=(aAND b)$:$n$.$m$和$x$都是已知的. 题目要求统计有多少$(a,b)$满足: $\left\{ {\begin{array}{*{20}{c}}{a + b = n}\\{aXORb = m}\end{array}}