• 线段树区间修改+二进制 红球进黑洞


    区间异或+查询,题目链接:https://ac.nowcoder.com/acm/contest/275/C

    (tip需要前置技能区间修改)

    题意:输入一段数,之后输入2区间则异或:选择l和r,对l,r之间的数进行异或操作,输入1则区间查询

    题解

    n,m<1e5,所以如果每次修改都异或到底logn*n*m绝壁t了,然后查了一下发现一个神奇的操作:

    我们先开21个线段树(二进制21位表示作为异的数以及我们的数)

    然后每个sum子节点存该位下1的个数

    每个lazy子节点标记储存的是该节点是否被异或

    如果作为异或数的k在该位是0,则不操作,因为0异或任何一个数都是数本身,如果是1则进行区间修改

    下面是代码部分(对关键几步都有解释)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=1e5+70;
    ll a[maxn];
    int n,m;
    typedef struct{
    ll sum,lazy;
    }point;
    point tree[25][maxn<<2];
    void up(int id,int rt)
    {
    tree[id][rt].sum=tree[id][rt<<1].sum+tree[id][rt<<1|1].sum;
    }//统计子节点1的个数

    void pd(int id,int l,int r,int rt)
    {
    if(tree[id][rt].lazy==1){//初始为0,因为^1^1还是原数所以这里lazy的0,1情况说明子节点是否需要修改
    tree[id][rt].lazy=0;
    tree[id][rt<<1].lazy^=1;
    tree[id][rt<<1|1].lazy^=1;
    int mid=(l+r)>>1;
    tree[id][rt<<1].sum=(mid-l+1)-tree[id][rt<<1].sum;
    tree[id][rt<<1|1].sum=(r-mid)-tree[id][rt<<1|1].sum;
    }
    }//对子节点进行区间修改和区间查询

    void build(int id,int l,int r,int rt)
    {
    if(l==r){
    tree[id][rt].sum=((a[l]>>id)&1);//找地id位是否是1
    tree[id][rt].lazy=0;
    return;
    }
    int mid=(l+r)>>1;
    build(id,l,mid,rt<<1);
    build(id,mid+1,r,rt<<1|1);
    up(id,rt);
    return;
    }

    void update(int id,int L,int R,int l,int r,int rt)
    {
    if(L<=l&&r<=R){
    tree[id][rt].sum=(r-l+1)-tree[id][rt].sum;
    tree[id][rt].lazy^=1;
    return;
    }//查询区间在已知区间内则直接修改:区间长度-原本的1的个数即为异或后的个数
    int mid=(l+r)>>1;
    pd(id,l,r,rt);//下推标记,下推子节点的1的数量以及lazy状态
    if(L<=mid)update(id,L,R,l,mid,rt<<1);
    if(R>mid)update(id,L,R,mid+1,r,rt<<1|1);
    up(id,rt);
    return;
    }
    ll query(int id,int L,int R,int l,int r,int rt)
    {
    if(L<=l&&r<=R){
    return tree[id][rt].sum;
    }
    int mid=(l+r)>>1;
    pd(id,l,r,rt);//每次查找修改前不要忘了下推标记
    ll ans=0;
    if(L<=mid) ans+=query(id,L,R,l,mid,rt<<1);//查询的时候我们统计该位下有多少个1
    if(R>mid) ans+=query(id,L,R,mid+1,r,rt<<1|1);
    return ans;
    }
    int main()
    {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    scanf("%lld",&a[i]);
    for(int i=0;i<=20;i++){
    build(i,1,n,1);
    }//10的5次方,这里搞了21位是防止临界出锅
    while(m--){
    ll t,l,r,k;
    scanf("%lld",&t);
    if(t==1){
    scanf("%lld%lld",&l,&r);
    ll ans=0;
    for(int i=0;i<=20;i++){
    ans+=1ll*query(i,l,r,1,n,1)*(1ll<<i);//1<<i会爆int
    }
    printf("%lld ",ans);
    }
    else{
    scanf("%lld%lld%lld",&l,&r,&k);
    for(int i=0;i<=20;i++){
    if((k>>i)&1){//这个&1很精髓
    update(i,l,r,1,n,1);
    }
    }
    }
    }
    return 0;
    }

  • 相关阅读:
    机器学习--强化学习
    机器学习--深度学习
    机器学习--维度灾难
    机器学习--最优化
    机器学习--降维
    机器学习--聚类
    机器学习--模型提升
    Git和gitHub用户名 邮箱
    Git线上操作
    版本控制器:Git
  • 原文地址:https://www.cnblogs.com/ilikeeatfish/p/11502966.html
Copyright © 2020-2023  润新知