• 牛客算法周周练11C


    链接:https://ac.nowcoder.com/acm/contest/6046/C
    来源:牛客网

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

    题目描述

    在心理疏导室中有一种奇特的疏导工具,叫做红球。红球被提前分为了许多正方形小方格。
    每当有人来找ATB做心理疏导时,ATB就会让他去先玩红球,然后通过红球小格方的高度来判断一个人的压力程度的高低
    具体地讲,ATB会让该人对于一个序列执行以下操作

    1. 区间求和,即输入l,r,输出xl+…xr的和
    2. 区间异或,即输入l,r,k,对于l ≤ i ≤ r,xi⊕k;
      可是ATB天天算计那么多答案,已经对这份工作产生了厌烦,所以请你帮帮他,对于一组给定的数据,输出对应的答案
      ATB会将你感谢到爆

    输入描述:

    第一行两个整数n和m,表示数列长度和询问次数
    第二行有n个整数,表示这个数列的初始数值
    接下来有m行,形如 1 l r 或者 2 l r k
    分别表示查询

    输出描述:

    对于每一个查询操作,输出查询的结果并换行
    示例1
    输入
    10 10
    8 5 8 9 3 9 8 3 3 6
    2 1 4 1
    1 2 6
    2 9 10 8
    1 1 7
    2 4 7 8
    2 8 8 6
    2 2 3 0
    1 1 2
    2 9 10 4
    1 2 3
    输出
    33
    50
    13
    13
    备注:

    1. 数据范围
      对于30%30%的数据,保证 n, m, k≤ 10
      对于另外30%30%的数据,保证 n, m ≤ 50000, k ∈ {0, 1}
      对于全部100%100%的数据,保证 1 ≤ n,m ≤ 105, 0≤ ai,k ≤ 105
    2. 说明
      a⊕b表示a xor b

    题目大意:

    第一行给出n m,表示n个数和m个询问,对于接下来的m行

    • 1 l r 表示求区间[l,r] 的和
    • 2 l r k 表示区间[l,r] 内每个数都异或k。

    解题思路:

    二维线段树+区间异或+区间求和,lazy标记改一下即可,开一个tree[max*4][25]的数组模拟线段树,25表示存放1的个数,方便lazy数组使用,通过位运算推出每个node 的值来,先放代码:

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    using namespace std;
    const int _max = 1e5+50;
    typedef long long ll;
    ll tree[_max<<2][25],lazy[_max<<2],a[_max];
    void pushup(int node)
    {
    	for(int i=0;i<25;i++)
    	  tree[node][i]=tree[node<<1][i]+tree[node<<1|1][i];
    }
    void build(int node,int l,int r)
    {
    	lazy[node]=0;
    	if(l==r)
    	{
    		for(int i=0;i<24;i++)
    		  ((ll)(1<<i)&a[l])?tree[node][i]=1:tree[node][i]=0;
    		return;
    	}
    	int mid=(l+r)>>1;
    	build(node<<1,l,mid);
    	build(node<<1|1,mid+1,r);
    	pushup(node);
    }
    void pushdown(int node,int l,int r)
    {
    	int mid=(l+r)>>1;
    	if(lazy[node])
    	{
    		lazy[node<<1|1]^=lazy[node];
    		lazy[node<<1]^=lazy[node];
    		for(int i=0;i<25;i++)
    		{
    			int t=(lazy[node]>>i)&1;
    			if(t)
    			{
    				tree[node<<1][i]=mid-l+1-tree[node<<1][i];
    				tree[node<<1|1][i]=r-mid-tree[node<<1|1][i];
    			}
    		}
    		lazy[node]=0;
    	}
    }
    void update(int node,int l,int r,int st,int en,int val)
    {
    	int mid=(l+r)>>1;
    	if(st<=l&&r<=en)
    	{
    		lazy[node]^=val;
    		for(int i=0;i<25;i++)
    		{
    			int t=(val>>i)&1;
    			if(t)
    			  tree[node][i]=r-l+1-tree[node][i];
    		}
    		return ;
    	}
    	pushdown(node,l,r);
    	if(st<=mid)
    	  update(node<<1,l,mid,st,en,val);
    	if(en>mid)
    	  update(node<<1|1,mid+1,r,st,en,val);
    	pushup(node);
    }
    ll query(int node,int l,int r,int st,int en)
    {
    	if(st<=l&&en>=r)
    	{
    		ll ans=0;
    		for(int i=0;i<25;i++)
    		  ans+=(ll)(1<<i)*tree[node][i];
    		return ans;
    	}
    	pushdown(node,l,r);
    	int mid=(l+r)>>1;
    	ll ans1=0,ans2=0;
    	if(st<=mid)
    	  ans1=query(node<<1,l,mid,st,en);
    	if(en>mid)
    	  ans2=query(node<<1|1,mid+1,r,st,en);
    	return ans1+ans2;
    }
    int main()
    {
    	int n,m;
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)
    	  scanf("%d",&a[i]);
    	build(1,1,n);
    	while(m--)
    	{
    		int f,l,r,k;
    		scanf("%d",&f);
    		if(f==1)
    		{
    			scanf("%d%d",&l,&r);
    			cout<<query(1,1,n,l,r)<<endl;
    		}
    		else
    		{
    			scanf("%d%d%d",&l,&r,&k);
    			update(1,1,n,l,r,k);
    		}
    	}
    	//system("pause");
    	return 0;
    }
    

    对pushdown函数的解释:pushdown向下更新,对于这里的for循环,异或的本质:1和0异或还是1,但和1异或就成了0。0和任何数异或都不变,所以0不考虑。也就是说,对于第 k 位,异或后1的个数 = 原来0的个数 = 区间长度(总个数)- 异或前1的个数, 所以就有了tree[node>>1][i] = mid-l+1-tree[node>>1][i]这个语句。

  • 相关阅读:
    Java 重写(Override)与重载(Overload)
    Java 继承
    Java 异常处理
    Java Scanner 类
    Java 流(Stream)、文件(File)和IO
    Java 方法
    Java 正则表达式
    Beta冲刺——代码规范、冲刺任务与计划
    Beta冲刺——凡事预则立
    Beta冲刺——问题总结博客(事后诸葛亮和组员交换事宜)
  • 原文地址:https://www.cnblogs.com/Hayasaka/p/14294250.html
Copyright © 2020-2023  润新知