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
,那么我可以是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;