BZOJ.5312.冒险(线段树)

题目链接

\(Description\)

维护一个序列,支持区间and/or一个数、区间查询最大值。

\(Solution\)

维护区间最大值?好像没什么用,修改的时候和暴力差不多。
我们发现有时候区间and/or对区间是没有影响的,有时候对区间所有数影响相同(都改变了某些位)。
比如区间and x,当\(sum_{or}\ and\ x==sum_{or}\)时,这个操作没有影响;
当区间所有数在x为1的位上相同,即x没有它们不共有的1,则可以直接打and/or标记,mx[rt] and/or= x。(\(sum_{and}\oplus sum_{or}\)就是区间内不共有的1)
(这个在and的时候也对啊,最大值and后还是>=其它数的。但是我还是写另一种方法吧。。即区间与和区间或修改后改变量都相同。)
在不满足这两个条件时继续递归子区间。

复杂度为\(O(nklogn)\)\(k\)为位数。证明参见:https://csacademy.com/contest/round-70/task/and-or-max/solution(浏览器打不开 别的浏览器没插件就不看了)

另外区间and/or标记可以换成区间加标记。

//13528kb   4472ms
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 200000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
const int N=200005<<2;

char IN[MAXIN],*SS=IN,*TT=IN;
inline int read()
{
    int now=0;register char c=gc();
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);now=now*10+c-'0',c=gc());
    return now;
}
struct Segment_Tree
{
    #define ls rt<<1
    #define rs rt<<1|1
    #define lson l,m,ls
    #define rson m+1,r,rs
    int sand[N],sor[N],mx[N],tag[N];

    #define Update(rt) sand[rt]=sand[ls]&sand[rs],sor[rt]=sor[ls]|sor[rs],mx[rt]=std::max(mx[ls],mx[rs])
    void Build(int l,int r,int rt)
    {
        if(l==r) {sand[rt]=sor[rt]=mx[rt]=read(); return;}
        Build(l,(l+r>>1),ls), Build((l+r>>1)+1,r,rs);
        Update(rt);
    }
    inline void Upd(int rt,int v)
    {
        sand[rt]+=v, sor[rt]+=v, tag[rt]+=v, mx[rt]+=v;
    }
    inline void PushDown(int rt)//非叶 
    {
        Upd(ls,tag[rt]), Upd(rs,tag[rt]), tag[rt]=0;
    }
    void Modify_And(int l,int r,int rt,int L,int R,int v)
    {
        if((sor[rt]&v)==sor[rt]) return;//==优先级比&,|高。。
        if(L<=l && r<=R && (sand[rt]&v)-sand[rt]==(sor[rt]&v)-sor[rt])//!((sand[rt]^sor[rt])&v))//为什么不对呢。。
        {
            Upd(rt,(sand[rt]&v)-sand[rt]); return;
        }
        if(tag[rt]) PushDown(rt);
        int m=l+r>>1;
        if(L<=m) Modify_And(lson,L,R,v);
        if(m<R) Modify_And(rson,L,R,v);
        Update(rt);
    }
    void Modify_Or(int l,int r,int rt,int L,int R,int v)
    {
        if((sand[rt]|v)==sand[rt]) return;
        if(L<=l && r<=R && (sand[rt]|v)-sand[rt]==(sor[rt]|v)-sor[rt])//!((sand[rt]^sor[rt])&v))
        {
            Upd(rt,(sand[rt]|v)-sand[rt]); return;
        }
        if(tag[rt]) PushDown(rt);
        int m=l+r>>1;
        if(L<=m) Modify_Or(lson,L,R,v);
        if(m<R) Modify_Or(rson,L,R,v);
        Update(rt);
    }
    int Query(int l,int r,int rt,int L,int R)
    {
        if(L<=l && r<=R) return mx[rt];
        if(tag[rt]) PushDown(rt);
        int m=l+r>>1;
        if(L<=m)
            if(m<R) return std::max(Query(lson,L,R),Query(rson,L,R));
            else return Query(lson,L,R);
        return Query(rson,L,R);
    }
}T;

int main()
{
    int n=read(),m=read();
    T.Build(1,n,1);
    for(int opt,l,r; m--; )
    {
        if((opt=read())==1) l=read(),r=read(),T.Modify_And(1,n,1,l,r,read());
        else if(opt==2) l=read(),r=read(),T.Modify_Or(1,n,1,l,r,read());
        else l=read(),r=read(),printf("%d\n",T.Query(1,n,1,l,r));
    }
    return 0;
}

转载于:https://www.cnblogs.com/SovietPower/p/9634876.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值