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
,那么我可以是1xxxx
,011xx
,这样。也就是说我从高到低去看:一方面是b[]
的这位能不能凑出1
,另一方面是 \(v-1\) 的这一位是多少。
- 如果只能凑出0就没什么好说了,如果能凑出1,那我希望\(\max a\) 尽量小,就要去比较左右两边用哪边的 1 更好,所以需要维护一个当前扩展出去的区间,以及涉及到一个静态RMQ,用st表处理。
核心代码:
- 如果只能凑出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;