[2018雅礼省选集训4-2]Problem A 位运算+势能分析线段树

首先把 看成把某些位强制变为 0 0 ,把看成把某些位强制变为 1 1 。那么对于一次修改,如果一个区间内的数这些位都相同就打标记,否则暴力处理左右区间即可。
先证明这样做的复杂度是对的,设fi=flsoni+frsoni+k=020[ik],那么 f(root) f ( r o o t ) 一开始是 O(20N) O ( 20 N ) 的,每次修改最多增加 O(20logN) O ( 20 log ⁡ N ) ,而每次访问一个新节点 f(root) f ( r o o t ) 至少减少 1 1 ,所以复杂度是O(20NlogN)的。
至于打标记,可以用一个与标记和一个或标记来表示(注意认为规定先后顺序),判断一些位是否全相同,可以维护区间与和、区级或和,与和为 1 1 或者或和为0的位就是全相同的了。
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define mid (l+r>>1)
#define N 200010
using namespace std;
const int R=(1<<20)-1;
int n,q,a[N];
int read()
{
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
struct tree
{
    int mx,h,y,bh,by;
    tree *ls,*rs;
    tree(){h=0;y=R;}
    void update()
    {
        mx=max(ls->mx,rs->mx);
        bh=ls->bh|rs->bh;
        by=ls->by&rs->by;
    }
    void cal(int dh,int dy)
    {
        mx=(mx&dy)|dh;bh=(bh&dy)|dh;by=(by&dy)|dh;
        y=(y&dy);h=(h&dy)|dh;
    }
    void pushdown()
    {
        ls->cal(h,y);
        rs->cal(h,y);
        h=0;y=R;
    }
    void build(int l,int r)
    {
        if(l==r) {mx=bh=by=a[l];return ;}
        (ls=new tree)->build(l,mid);
        (rs=new tree)->build(mid+1,r);
        update();
    }
    void mdf(int l,int r,int lx,int rx,int dh,int dy)
    {
        if(l==lx&&r==rx)
        {
            int t=(by|(R^bh));
            if(((dh|t)==t)&&(((R^dy)|t)==t)) {cal(dh,dy);return ;} 
        }
        pushdown();
        if(rx<=mid) ls->mdf(l,mid,lx,rx,dh,dy);
        else if(lx>mid) rs->mdf(mid+1,r,lx,rx,dh,dy);
        else ls->mdf(l,mid,lx,mid,dh,dy),rs->mdf(mid+1,r,mid+1,rx,dh,dy);
        update();
    }
    int qry(int l,int r,int lx,int rx)
    {
        if(l==lx&&r==rx) return mx;
        pushdown();
        if(rx<=mid) return ls->qry(l,mid,lx,rx);
        if(lx>mid) return rs->qry(mid+1,r,lx,rx);
        return max(ls->qry(l,mid,lx,mid),rs->qry(mid+1,r,mid+1,rx)); 
    }
}*xtr;
int main()
{
    n=read();q=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    (xtr=new tree)->build(1,n); 
    while(q--)
    {
        int opt=read(),l=read(),r=read(),x;
        if(opt==1)  xtr->mdf(1,n,l,r,0,read());
        if(opt==2)  xtr->mdf(1,n,l,r,read(),R);
        if(opt==3)  printf("%d\n",xtr->qry(1,n,l,r));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值