• BZOJ5312 冒险(势能线段树)


    BZOJ题目传送门

    表示蒟蒻并不能一眼看出来这是个势能线段树。

    不过仔细想想也并非难以理解,感性理解一下,在一个区间里又与又或,那么本来不相同的位也会渐渐相同,线段树每个叶子节点最多修改(log a)次((a)为值域)。

    那么,我们做区间修改的时候,进行判断:如果这一次修改对区间里所有数的影响都是一样的,那么直接在当前位置放懒标记。

    如何判断呢?又是一个位运算技巧:维护区间与和区间或,两者的异或即为区间内存在不同的位集。那么只有这些位集不会被与上0、或上1,才可以放懒标记。

    至于又与又或很麻烦,我们定义标记((la,lo))表示整个区间都&la|lo

    标记的合并手推一下就好了,((la,lo)+(na,no)=(la&na,lo&na|no))

    复杂度(nlog nlog a),果然维护的东西一多数组版线段树的常数就大起来了。。。

    #include<bits/stdc++.h>
    #define RG register
    #define R RG int
    #define G if(++ip==ie)fread(ip=buf,1,N,stdin)
    using namespace std;
    const int N=1<<19,S=(1<<21)-1;
    char buf[N],*ie=buf+N,*ip=ie-1;
    int na,no,sa[N],so[N],la[N],lo[N],mx[N];
    inline int in(){
    	G;while(*ip<'-')G;
    	R x=*ip&15;G;
    	while(*ip>'-'){x*=10;x+=*ip&15;G;}
    	return x;
    }
    #define Pushup									
    	sa[x]=sa[lc]&sa[rc];						
    	so[x]=so[lc]|so[rc];						
    	mx[x]=max(mx[lc],mx[rc])
    #define Pushdn									
    	if(la[x]!=S||lo[x]){						
    		pusht(lc,la[x],lo[x]);					
    		pusht(rc,la[x],lo[x]);					
    		la[x]=S;lo[x]=0;						
    	}
    inline void pusht(R x,R a,R o){//合并标记并更新信息
    	la[x]&=a;(lo[x]&=a)|=o;
    	(so[x]&=a)|=o;(sa[x]&=a)|=o;(mx[x]&=a)|=o;
    }
    void build(R x,R l,R r){
    	la[x]=S;
    	if(l==r){
    		mx[x]=sa[x]=so[x]=in();return;
    	}
    	R m=(l+r)>>1,lc=x<<1,rc=lc|1;
    	build(lc,l,m);build(rc,m+1,r);
    	Pushup;
    }
    void upd(R x,R l,R r,R s,R e){
    	if(l==s&&r==e&&!((sa[x]^so[x])&(~na|no)))//判断是否影响一致
    		return pusht(x,na,no);
    	R m=(l+r)>>1,lc=x<<1,rc=lc|1;
    	Pushdn;
    	if(e<=m)upd(lc,l,m,s,e);
    	else if(s>m)upd(rc,m+1,r,s,e);
    	else upd(lc,l,m,s,m),upd(rc,m+1,r,m+1,e);
    	Pushup;
    }
    int qry(R x,R l,R r,R s,R e){
    	if(l==s&&r==e)return mx[x];
    	R m=(l+r)>>1,lc=x<<1,rc=lc|1;
    	Pushdn;
    	if(e<=m)return qry(lc,l,m,s,e);
    	if(s>m)return qry(rc,m+1,r,s,e);
    	return max(qry(lc,l,m,s,m),qry(rc,m+1,r,m+1,e));
    }
    int main(){
    	R n=in(),m=in(),op,l,r;
    	build(1,1,n);
    	while(m--){
    		op=in();l=in();r=in();
    		if(op==1)na=in(),no=0,upd(1,1,n,l,r);//或上0还是原来的数
    		if(op==2)na=S,no=in(),upd(1,1,n,l,r);//与上全1还是原来的数
    		if(op==3)printf("%d
    ",qry(1,1,n,l,r));
    	}
    	return 0;
    }
    
  • 相关阅读:
    攻克python3-进程
    攻克python3-线程
    攻克python3-socket
    攻克python3-面向对象
    攻克python3-装饰器
    攻克python3-函数
    攻克python3-文件操作
    算法基础
    MongoDB存储基础教程
    Python操作Excle
  • 原文地址:https://www.cnblogs.com/flashhu/p/9686599.html
Copyright © 2020-2023  润新知