• 题解 Luogu P5607 [Ynoi2013] 无力回天 NOI2017


    题意

    给定长度为 \(n\) 的整数序列 \(a_1,a_2,\cdots,a_n\),要求支持 \(m\) 次操作,每次操作是以下两种之一:

    • 给定 \(l,r,v\),将区间 \([l,r]\) 异或上 \(v\)
    • 给定 \(l,r,v\),查询区间 \([l,r]\) 内选出任意个数的 \(a_i\),这些数和 \(v\) 的最大异或和(可以不选)

    \(1 \leq n,m \leq 5 \times 10^4,0 \leq a_i,v\leq 10^9,1 \leq l \leq r \leq n\)

    题解

    注意到区间异或之后可以维护的信息非常有限,考虑差分,记 \(b_i = a_i \operatorname{xor} a_{i-1}\)

    那么区间异或相当于把 \(b_l,b_{r+1}\) 异或上 \(v\)

    考虑查询,可以用 0-1 Trie 或者线性基。但是区间 0-1 Trie 肯定存不下,于是只能线性基。

    然后有一个聪明的性质,\(a_l,a_{l+1},\cdots,a_r\) 的线性基和 \(a_l,b_{l+1},\cdots,b_r\) 相同。

    稍加观察就可以发现右边能表出的数一定是若干个 \(a_i(l \leq i \leq r)\) 异或起来,所以右边能被左边表出。

    左边能表出的是若干个 \(a_i\) 的异或和,那么只要能够证明右边可以表出任意一个 \(a_i\),就可以证明右边可以表出任意个 \(a_i\) 的异或和。我们只需要把表出单个 \(a_i\) 的方案拼到一起,最后只留下在方案中出现了奇数次的数,就得到了表出这些 \(a_i\) 的方案。

    事实上这是非常简单的。对于 \(a_l\),就选 \(a_l\)。对于后面的,就是选 \(a_l\) 和一段 \(b_i\) 异或起来。

    然后?维护区间线性基,支持单点修改区间查询,因为两个线性基合并是 \(O(\log^2V)\) 的,于是复杂度是 \(O(n \log n \log^2 V)\)

    一个线性基小技巧:合并两个线性基的正确姿势是在一个里面插入另外一个,而不是建一个新的线性基憨憨地把两边都插进去,常数大到飞起。

    甚至还有一种更优秀的写法。

    inline void merge(Base &cur,Base lc,Base rc){
    	cur.clear();
    	for(int i=30;i>=0;--i)
    		if(lc.a[i]) cur.a[i]=lc.a[i];
    		else cur.a[i]=rc.a[i];
    	for(int i=30;i>=0;--i) if(lc.a[i]&&rc.a[i]) cur.ins(rc.a[i]);
    	return;
    }
    

    即:先尝试只用左边或右边的数表出某些位,对于那些两边都有值的位,再尝试两边的数都用,看能不能表出更多。

    # include <bits/stdc++.h>
    
    const int N=50010,INF=0x3f3f3f3f;
    
    struct Base{
    	int a[32];
    	inline void ins(int x){
    		for(int i=30;i>=0;--i){
    			if(x&(1<<i)){
    				if(a[i]) x^=a[i];
    				else{
    					a[i]=x;
    					break;
    				}
    			}
    		}
    		return;
    	}
    	inline void clear(void){
    		memset(a,0,sizeof(a));
    		return;
    	}
    }tree[N<<2];
    int xsum[N<<2];
    int a[N],b[N];
    int n,m;
    
    inline int read(void){
    	int res,f=1;
    	char c;
    	while((c=getchar())<'0'||c>'9')
    		if(c=='-')f=-1;
    	res=c-48;
    	while((c=getchar())>='0'&&c<='9')
    		res=res*10+c-48;
    	return res*f;
    }
    inline int lc(int x){
    	return x<<1;
    }
    inline int rc(int x){
    	return x<<1|1;
    }
    inline void merge(Base &cur,Base lc,Base rc){
    	cur.clear();
    	for(int i=30;i>=0;--i)
    		if(lc.a[i]) cur.a[i]=lc.a[i];
    		else cur.a[i]=rc.a[i]; 
    	for(int i=30;i>=0;--i) if(lc.a[i]&&rc.a[i]) cur.ins(rc.a[i]);
    	return;
    }
    inline void pushup(int k){
    	merge(tree[k],tree[lc(k)],tree[rc(k)]);
    	xsum[k]=xsum[lc(k)]^xsum[rc(k)];
    	return;
    }
    void build(int k,int l,int r){
    	if(l==r){
    		tree[k].ins(b[l]),xsum[k]=b[l];
    		return;
    	}
    	int mid=(l+r)>>1;
    	build(lc(k),l,mid),build(rc(k),mid+1,r);
    	pushup(k);
    	return;
    }
    void change(int k,int l,int r,int x,int v){
    	if(l==r){
    		tree[k].clear(),xsum[k]=(b[l]^=v),tree[k].ins(b[l]);
    		return;
    	}
    	int mid=(l+r)>>1;
    	if(x<=mid) change(lc(k),l,mid,x,v);
    	else change(rc(k),mid+1,r,x,v);
    	pushup(k);
    	return;
    }
    int queryval(int k,int l,int r,int L,int R){
    	if(L<=l&&r<=R) return xsum[k];
    	int mid=(l+r)>>1,res=0;
    	if(L<=mid) res^=queryval(lc(k),l,mid,L,R);
    	if(mid<R) res^=queryval(rc(k),mid+1,r,L,R);
    	return res;
    }
    Base querybase(int k,int l,int r,int L,int R){
    	if(L>R){
    		return tree[0];
    	}
    	if(L<=l&&r<=R) return tree[k];
    	int mid=(l+r)>>1;
    	Base res;
    	if((L<=mid)&&!(mid<R)) return querybase(lc(k),l,mid,L,R);
    	if(!(L<=mid)&&mid<R) return querybase(rc(k),mid+1,r,L,R);
    	merge(res,querybase(lc(k),l,mid,L,R),querybase(rc(k),mid+1,r,L,R));
    	return res;
    }
    
    int main(void){
    	n=read(),m=read();
    	for(int i=1;i<=n;++i) a[i]=read(),b[i]=a[i]^a[i-1];
    	build(1,1,n);
    	int op,l,r,v;
    	while(m--){
    		op=read(),l=read(),r=read(),v=read();
    		if(op==1){
    			change(1,1,n,l,v);
    			if(r<n) change(1,1,n,r+1,v);
    		}else{
    			Base res=querybase(1,1,n,l+1,r);
    			int al=queryval(1,1,n,1,l);
    			res.ins(al);
    			for(int i=30;i>=0;--i) v=std::max(v,v^res.a[i]);
    			printf("%d\n",v);
    		}
    	}
    
    	return 0;
    }
    
    
  • 相关阅读:
    源码安装jdk
    yum操作的一些笔记
    Tomcat笔记
    源码编译安装zabbix server端和agent端
    用nginx做反向代理时 通过设置让后台真实服务器日志记录客户端的IP
    LVS负载均衡的两种调度模式:NAT和DR
    nginx配置文件详解
    FPGA高级设计——时序分析和收敛(转)
    12个有趣的C语言面试题
    LDO稳压器工作原理
  • 原文地址:https://www.cnblogs.com/liuzongxin/p/16546595.html
Copyright © 2020-2023  润新知