• UOJ671 【UNR #5】诡异操作


    Description

    区间加,区间去 \(\rm AND\) ,区间求和

    \(n\le 3\times 10^5,Q\le 2\times 10^5,a_i,v\le 2^{128}\)

    Solution

    特判除数 \(v=1\) 的情况,那么暴力进行除法操作复杂度就是 \(\Theta(n\log V)\) ,可以接受

    常规思路维护 \(\log V\) 个二进制数表示区间里面 \(a_i\) 在第 \(1\sim \log V\) 位中为 \(1\) 的数量是多少,但是复杂度 \(\Theta(n\log^2V+q\log V\log n)\) ,不能通过

    注意到在常规思路中线段树每个节点本质上维护的是一个 \(\log V\times \log n\) 的数表

    第二维大小是 \(\log n\) 的原因是数字数量不会超过 \(n\),更严格的,应当为 \(\log len\)

    那么可以将这个数表进行翻转!现在变成维护 \(\log n\) 个长度为 \(\log V\) 的二进制数

    区间求和就是 \(val_i\times2^{i}\) ,除法计算时直接使用 \(val_0\) 就是真实的节点权值,甚至不用重构叶子处的数表,取 \(\rm AND\) 就是对所有 \(val_i\) 进行操作

    剩余的唯一问题就是 push_up 操作中数表本身的维护,注意到每位的权值是独立的,那么维护第 \(i-1\) 位两个子区间向量的进位情况 \(\rm carry\)(压缩成一个长度为 \(128\)\(0/1\) 串)

    不难发现 \(val_i\) 就是 \(\displaystyle val_{\rm lson}\oplus val_{\rm rson}\oplus \rm carry\) ,而在两者加法中完成进位的不会再进一次,或起来即可

    时间复杂度降至 \(\Theta(n\log V+q\log^2n)\) ,需要加入的常数优化有二:

    • 数表的大小是 \(\log len\) ,所以需要使用给每个节点在内存池中分配空间而不是开到 \(N\times \log n\) ,注意总空间是 \(\sum 2^i\log\lfloor \frac{n}{2^i}\rfloor\le n\sum\frac{i}{2^i}\approx n\)

    • 区间全零后在区间取 \(\rm AND\) 和区间求和也可以进行剪枝

    Code

    const int N=3e5+10;
    int n,Q,num[N<<2];
    u128 Memory_pool[N*5+1000],*indic;
    u128 a[N],tag[N<<2],*val[N<<2];
    bool zero[N<<2];
    #define ls p<<1
    #define rs p<<1|1
    #define lson p<<1,l,mid
    #define rson p<<1|1,mid+1,r
    inline void push_up(int p){
    	u128 carry=0;
    	for(int i=0;i<=num[p];++i){
    		u128 x=i<=num[ls]?val[ls][i]:0;
    		u128 y=i<=num[rs]?val[rs][i]:0;
    		val[p][i]=x^y^carry;
    		carry=((x^y)&carry)|(x&y);
    	}
    	zero[p]=zero[ls]&zero[rs];
    	return ;
    }
    inline void push_tag(int p,u128 v){
    	if(zero[p]) return ;
    	tag[p]&=v;
    	rep(i,0,num[p]) val[p][i]&=v;
    	return ;
    }
    inline void push_down(int p){
    	if(~tag[p]){
    		push_tag(ls,tag[p]);
    		push_tag(rs,tag[p]);
    		tag[p]=-1;
    	} return ;
    }
    inline void build(int p,int l,int r){
    	tag[p]=-1; num[p]=31-__builtin_clz(r-l+1);
    	val[p]=indic; indic+=num[p]+1;
    	if(l==r){
    		if(!(val[p][0]=a[l])) zero[p]=1;
    		return ;
    	}
    	int mid=(l+r)>>1;
    	build(p<<1,l,mid); build(p<<1|1,mid+1,r);
    	return push_up(p);
    }
    inline u128 Query(int st,int ed,int p=1,int l=1,int r=n){
    	if(zero[p]) return 0;
    	if(st<=l&&r<=ed){
    		u128 res=0;
    		rep(i,0,num[p]) res+=val[p][i]<<i;
    		return res;
    	}
    	int mid=(l+r)>>1; push_down(p);
    	u128 res=0;
    	if(st<=mid) res+=Query(st,ed,lson);
    	if(ed>mid) res+=Query(st,ed,rson);
    	return res;
    }
    inline void Div(int st,int ed,u128 v,int p=1,int l=1,int r=n){
    	if(zero[p]) return ;
    	if(l==r){
    		zero[p]=!(val[p][0]/=v);
    		return ;
    	}
    	int mid=(l+r)>>1; push_down(p);
    	if(st<=mid) Div(st,ed,v,lson);
    	if(ed>mid) Div(st,ed,v,rson);
    	return push_up(p);
    }
    inline void And(int st,int ed,u128 v,int p=1,int l=1,int r=n){
    	if(zero[p]) return ;
    	if(st<=l&&r<=ed) return push_tag(p,v);
    	int mid=(l+r)>>1; push_down(p);
    	if(st<=mid) And(st,ed,v,lson);
    	if(ed>mid) And(st,ed,v,rson);
    	return push_up(p);
    }
    #undef ls
    #undef rs
    #undef lson
    #undef rson
    signed main(){
    	n=read(); Q=read();	
    	for(int i=1;i<=n;++i) a[i]=read_16();
    	indic=Memory_pool;
    	build(1,1,n);
    	while(Q--){
    		int opt=read(),l=read(),r=read();
    		if(opt==1){
    			u128 v=read_16();
    			if(v!=1) Div(l,r,v);	
    		}
    		if(opt==2){
    			u128 v=read_16();
    			And(l,r,v);	
    		}
    		if(opt==3) output(Query(l,r)),putchar('\n');
    	}
    	return 0;
    }
    
  • 相关阅读:
    Android ELF文件格式
    AWS发布架构师认证的专业解决方案
    python django模型内部类meta详细解释
    SQL_substr功能测试
    猫学习IOS(四)UI半小时就搞定Tom猫
    使用jquery点击一个实现button或连接,进行以下div显示,在点击隐藏
    hdu2852--KiKi&#39;s K-Number(段树,求第一k的数量)
    信息安全官谁:逼近的挑战,你准备好了吗?
    VS2010使整个过程说明了安装包
    进程的创建与可执行程序的加载
  • 原文地址:https://www.cnblogs.com/yspm/p/16315304.html
Copyright © 2020-2023  润新知