CF1936D. Bitwise Paradox-位运算、分治、线段树

link:https://codeforces.com/contest/1936/problem/D
题意:给两个长度为 \(n\) 的序列 \(a\)\(b\),对一个区间 \([l,r]\),称 \([l,r]\)的,若:\((b_l|\dots|b_r)\geq v\)。一个区间的 美丽值 定义为区间内 \(a\) 的最大值。现在有 \(q\) 个操作:

  • 1、\(b_i\to x\).
  • 2、给 \(l,r\),问 \([l,r]\) 的所有的子区间 \([l_0,r_0]\) ,美丽值最小的是多少?

分析性质:

  • 1、如果某个区间 \([l,r]\) 是好的,则更大的区间 \([L,R]\)\([l,r]\subset [L,R]\)) 一定是好的,同时因为美丽值是max,因此一定会更大,而我们希望的是最小的美丽值,因此真正关心的是一个个极小的好区间
  • 2、对于 \(\geq v\) 的位运算问题,可以改写成 \(>v-1\),然后拆位考虑,具体见3
  • 3、对于区间的子区间问题,会想到最大子段和问题(同样是问区间的子区间):\([l,r]\) 的答案,要么来自于左半边,要么来自右半边,要么来自某个跨过中点的区间。前两个递归解决,考察最后一个情况:
    • 假设从中点向着两边扩展,我希望找到一些 或和\(>v-1\) 的区间,如果只看一个方向,首先要注意到一个性质(我场内就没注意到…)——按位或是单调不减的,而每次变大一定是某个二进制的 \(0\to 1\),因此会让按位或变大的位置,只有\(O(\log V)\) 个。

    • 从左到右和从右到左是类似的,因此我们考虑用线段树维护两个长度为31的数组 pre,suf,表示区间从两个方向扩展,二进制的某一位为 \(1\) 的第一个位置,这个信息很好合并。

    • 答案怎么更新?就是看拆位,我们想的时候举个栗子,比如要严格大于二进制数:01011,那么我可以是1xxxx011xx,这样。也就是说我从高到低去看:一方面是b[]的这位能不能凑出1,另一方面是 \(v-1\) 的这一位是多少。

      • 如果只能凑出0就没什么好说了,如果能凑出1,那我希望\(\max a\) 尽量小,就要去比较左右两边用哪边的 1 更好,所以需要维护一个当前扩展出去的区间,以及涉及到一个静态RMQ,用st表处理。
        核心代码:
sgt operator +(const sgt &ls,const sgt &rs){
    sgt ret;
    int L=ls.r;ret.l=ls.l;
    int R=rs.l;ret.r=rs.r;
    ret.v=min(ls.v,rs.v);
    for(int i=30;i>=0;i--){
        ret.pre[i]=(ls.pre[i]?ls.pre[i]:rs.pre[i]);
        ret.suf[i]=(rs.suf[i]?rs.suf[i]:ls.suf[i]);
    }
    for(int i=30;i>=0;i--){
        if(!ls.suf[i]&&!rs.pre[i]){
            if(digit[i])break;
            continue;
        }
        int _L=min(L,ls.suf[i]),_R=max(R,rs.pre[i]);
        if(digit[i]){
            if(!ls.suf[i])R=_R;
            else if(!rs.pre[i])L=_L;
            else{
                if(st.query(_L,ls.r)<st.query(rs.l,_R))L=_L;
                else R=_R;
            }
        }else{
            if(!ls.suf[i])ret.v=min(ret.v,st.query(L,_R));
            else if(!rs.pre[i])ret.v=min(ret.v,st.query(_L,R));
            else ret.v=min({ret.v,st.query(L,_R),st.query(_L,R)});
        }
    }
    return ret;
}

代码:

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define endl '\n'
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
const int N=2e5+5;
const int INF=0x3f3f3f3f;
int n,v,a[N],b[N];
bool digit[50];

struct ST{
    int f[21][N];
    void init(int *a,int n){
        rep(i,1,n)f[0][i]=a[i];
        for(int j=1;j<=20;j++)for(int i=1;i+(1<<j)-1<=n;i++)
            f[j][i]=max(f[j-1][i],f[j-1][i+(1<<(j-1))]);
    }
    int query(int l,int r){
        int k=__lg(r-l+1);
        return max(f[k][l],f[k][r+1-(1<<k)]);
    }
}st;
struct sgt{
    int suf[31],pre[31];
    int v,l,r;
    sgt(){
        memset(suf,0,sizeof(suf));
        memset(pre,0,sizeof(pre));
        v=INF;
    }
};
sgt operator +(const sgt &ls,const sgt &rs){
    sgt ret;
    int L=ls.r;ret.l=ls.l;
    int R=rs.l;ret.r=rs.r;
    ret.v=min(ls.v,rs.v);
    for(int i=30;i>=0;i--){
        ret.pre[i]=(ls.pre[i]?ls.pre[i]:rs.pre[i]);
        ret.suf[i]=(rs.suf[i]?rs.suf[i]:ls.suf[i]);
    }
    for(int i=30;i>=0;i--){
        if(!ls.suf[i]&&!rs.pre[i]){
            if(digit[i])break;
            continue;
        }
        int _L=min(L,ls.suf[i]),_R=max(R,rs.pre[i]);
        if(digit[i]){
            if(!ls.suf[i])R=_R;
            else if(!rs.pre[i])L=_L;
            else{
                if(st.query(_L,ls.r)<st.query(rs.l,_R))L=_L;
                else R=_R;
            }
        }else{
            if(!ls.suf[i])ret.v=min(ret.v,st.query(L,_R));
            else if(!rs.pre[i])ret.v=min(ret.v,st.query(_L,R));
            else ret.v=min({ret.v,st.query(L,_R),st.query(_L,R)});
        }
    }
    return ret;
}
struct segT{
    sgt tr[N<<2];
    #define ls (node<<1)
    #define rs (node<<1|1)
    void push_up(int node){tr[node]=tr[ls]+tr[rs];}
    void cl(sgt &p,int x){
        for(int i=30;i>=0;i--)p.suf[i]=p.pre[i]=(((b[x]>>i)&1)?x:0);
        p.v=(b[x]>v?a[x]:INF);
        p.l=p.r=x;
    }
    void build(int node,int l,int r){
        if(l==r){
            cl(tr[node],l);
            return;
        }
        int mid=(l+r)>>1;
        build(ls,l,mid);build(rs,mid+1,r);
        push_up(node);
    }
    void modify(int node,int l,int r,int x,int val){
        if(l==r){
            b[l]=val;
            cl(tr[node],l);
            return;
        }
        int mid=(l+r)>>1;
        if(mid>=x)modify(ls,l,mid,x,val);
        else modify(rs,mid+1,r,x,val);
        push_up(node);
    }
    sgt query(int node,int l,int r,int ql,int qr){
        if(ql<=l&&r<=qr)return tr[node];
        int mid=(l+r)>>1;
        if(mid<ql)return query(rs,mid+1,r,ql,qr);
        if(mid+1>qr)return query(ls,l,mid,ql,qr);
        return query(ls,l,mid,ql,qr)+query(rs,mid+1,r,ql,qr);
    }
}seg;
int main(){
    fastio;
    int tc;cin>>tc;
    while(tc--){
        cin>>n>>v;
        v--;
        for(int i=30;i>=0;i--)digit[i]=((v>>i)&1);
        rep(i,1,n)cin>>a[i];
        rep(i,1,n)cin>>b[i];
        st.init(a,n);
        seg.build(1,1,n);

        int q;cin>>q;
        while(q--){
            int op,i,x,l,r;
            cin>>op;
            if(op==1){
                cin>>i>>x;
                seg.modify(1,1,n,i,x);
            }else{
                cin>>l>>r;
                int ans=seg.query(1,1,n,l,r).v;
                if(ans==INF)cout<<-1<<' ';
                else cout<<ans<<' ';
            }
        }
        cout<<endl;
    }
    return 0;
posted @ 2024-07-12 10:47  yoshinow2001  阅读(12)  评论(0编辑  收藏  举报