zoukankan      html  css  js  c++  java
  • CF242E XOR on Segment (线段树)

    CF242E XOR on Segment

    这道题还蛮简单的。其实很经典。

    看到 xor,第一下想到的是 \(01\) Trie,但是很显然不支持区间修改。于是我们可以自然地想到线段树。我们在每个节点上可以维护当前区间的和,但是不是用一个数来表示,而是用二进制表示的。我们记 \(num_{1,i}\) 为这个区间第 \(i\) 位二进制有多少个 \(1\)\(num_{0,i}\) 同理。为了方便统计,就不用进位了。显然 pushup 操作很简单,这里不再赘述。现在如果要警醒修改,设修改的区间是 \([l,r]\),修改的数是 \(val\)。在修改时,只要当前 \(val\) 这一位上二进制是 \(1\),就把 \(num_{1,i},num_{0,i}\) 数值交换一下即可。最后统计答案时只需要把二进制加起来即可。

    时间复杂度 \(O(\log \{\max a_i\}\cdot n\log n)\)

    读者可以思考一下把 xor 换成 andor 怎么做。

    //Don't act like a loser.
    //This code is written by huayucaiji
    //You can only use the code for studying or finding mistakes
    //Or,you'll be punished by Sakyamuni!!!
    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    
    int read() {
    	char ch=getchar();
    	int f=1,x=0;
    	while(ch<'0'||ch>'9') {
    		if(ch=='-')
    			f=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9') {
    		x=x*10+ch-'0';
    		ch=getchar();
    	}
    	return f*x;
    }
    
    const int MAXN=1e5+10;
    
    int n,m;
    int a[MAXN];
    
    struct seg_tree {
    	int lazy,num[2][21];
    }s[MAXN<<2];
    
    void pushdown(int l,int r,int p) {
    	if(l==r) {
    		return ;
    	}
    	s[p<<1].lazy^=s[p].lazy;
    	s[p<<1|1].lazy^=s[p].lazy;
    	
    	for(int i=0;i<=20;i++) {
    		if(s[p].lazy&(1<<i)) {
    			swap(s[p<<1].num[0][i],s[p<<1].num[1][i]);
    			swap(s[p<<1|1].num[0][i],s[p<<1|1].num[1][i]);
    		}
    	}
    	s[p].lazy=0;
    }
    
    void build(int l,int r,int p) {
    	if(l==r) {
    		for(int i=0;i<=20;i++) {
    			s[p].num[a[l]&(1<<i)? 1:0][i]++;
    		}
    		s[p].lazy=0;
    		return ;
    	}
    	int mid=(l+r)>>1;
    	build(l,mid,p<<1);
    	build(mid+1,r,p<<1|1);
    	for(int i=0;i<=20;i++) {
    		s[p].num[1][i]=s[p<<1].num[1][i]+s[p<<1|1].num[1][i];
    		s[p].num[0][i]=s[p<<1].num[0][i]+s[p<<1|1].num[0][i];
    	}
    }
    void modify(int l,int r,int p,int x,int y,int val) {
    	pushdown(l,r,p);
    	if(r<x||y<l) {
    		return ;
    	}
    	if(x<=l&&r<=y) {
    		for(int i=0;i<=20;i++) {
    			if(val&(1<<i)) {
    				swap(s[p].num[0][i],s[p].num[1][i]);
    			}
    		}
    		s[p].lazy=val;
    		return ;
    	}
    	int mid=(l+r)>>1;
    	modify(l,mid,p<<1,x,y,val);
    	modify(mid+1,r,p<<1|1,x,y,val);
    	for(int i=0;i<=20;i++) {
    		s[p].num[1][i]=s[p<<1].num[1][i]+s[p<<1|1].num[1][i];
    		s[p].num[0][i]=s[p<<1].num[0][i]+s[p<<1|1].num[0][i];
    	}
    }
    
    int query(int l,int r,int p,int x,int y) {
    	pushdown(l,r,p);
    	if(r<x||y<l) {
    		return 0;
    	}
    	if(x<=l&&r<=y) {
    		int ret=0;
    		for(int i=0;i<=20;i++) {
    			ret+=(1<<i)*s[p].num[1][i];
    		}
    		return ret;
    	}
    	int mid=(l+r)>>1;
    	return query(l,mid,p<<1,x,y)+query(mid+1,r,p<<1|1,x,y);
    }
    
    signed main() {
    	cin>>n;
    	for(int i=1;i<=n;i++) {
    		a[i]=read();
    	}
    	build(1,n,1);
    	
    	cin>>m;
    	for(int i=1;i<=m;i++) {
    		int opt=read();
    		if(opt==2) {
    			int x=read(),y=read(),val=read();
    			modify(1,n,1,x,y,val);
    		}
    		else {
    			int x=read(),y=read();
    			printf("%lld\n",query(1,n,1,x,y));
    		}
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    剑指Offer:数组中的逆序对
    Java高并发教程:Java NIO简介
    Java高并发教程:高并发IO的底层原理
    算法相关——Java排序算法之希尔排序(五)
    Materialized View模式
    Java技术——Java中的static关键字解析
    算法相关——Java排序算法之插入排序(四)
    Android Studio 2.2新增布局——ConstraintLayout完全解析
    公平锁与非公平锁
    Java线程和多线程(十五)——线程的活性
  • 原文地址:https://www.cnblogs.com/huayucaiji/p/CF242E.html
Copyright © 2011-2022 走看看