kmjp's blog

競技プログラミング参加記です

Codeforces #930 : Div1 D. Bitwise Paradox

本番中に解けず…。
https://codeforces.com/contest/1936/problem/D

問題

整数列A,Bが与えられる。
定数vに対し区間[L,R]が良いとは、B[L]....B[R]のbitwise orがv以上であることを示す。
そのような区間の美しさとは、A[L]...A[R]の最大値である。

以下のクエリに順次答えよ。

  • B[i]を一点更新する
  • 区間[L,R]が与えられるので、ここに含まれる区間のうち、良い区間における美しさの最小値を答えよ。

解法

まずAのRMQを高速に求められるようにしておく。

その後、SegTreeでBの区間を管理する。
各ノードには、区間内で各bitの登場する最左及び最右の位置を保持しておく。
ここから、区間の始点及び終点の候補がわかるので、それぞれ良い区間を構成できるような終点・始点を求め、Aの最大値の最小値を探そう。

int T,N,V;
int A[202020],B[202020];

template<class V,int NV> class RMQ {
private:
	V table[NV+1][1<<NV];
	int LG[1<<NV];
public:
	int NV2;
	static V const def=-(1<<30);
	V comp(V l,V r){ return max(l,r);};
	RMQ() {
		int i,x;
		NV2=1<<NV;
		LG[1]=0;
		for(i=2;i<NV2;i++) LG[i]=LG[i/2]+1;
		FOR(i,NV) FOR(x,NV2) table[i][x]=def;
	}
	void set(int x,V v){ table[0][x]=v;}
	void build() {
		int i,j,x,y;
		FOR(i,NV) FOR(x,NV2) table[i+1][x]=comp(table[i][x],(x+(1<<i)<NV2)?table[i][x+(1<<i)]:def);
	}
	V query(int L,int R) { //[L,R),
		L=max(0,L), R=min(R,NV2);
		if(R<=L) return def;
		int WL=LG[R-L];
		return comp(table[WL][L],table[WL][R-(1<<WL)]);
	}
	
};
RMQ<int,18> rmq;

struct info {
	int L[30],R[30];
	int PL,PR;
	int ret;
};

info tmp;

template<class C,int NV> class SegTree_1 {
public:
	vector<C> val;
	C comp(C l,C r){
		if(l.PL==-1) return r;
		if(r.PL==-1) return l;
		C v;
		v.ret=min(l.ret,r.ret);
		v.PL=l.PL;
		v.PR=r.PR;
		int i;
		FOR(i,30) {
			v.L[i]=(l.L[i]==-1)?r.L[i]:l.L[i];
			v.R[i]=(r.R[i]==-1)?l.R[i]:r.R[i];
		}
		
		int CPL=l.PR;
		int CPR=r.PL;
		for(i=29;i>=0;i--) {
			int LV=(l.R[i]==-1)?1<<30:rmq.query(min(l.R[i],CPL),CPR+1);
			int RV=(r.L[i]==-1)?1<<30:rmq.query(CPL,max(r.L[i],CPR)+1);
			if(LV<RV) {
				if(V&(1<<i)) {
					if(LV<v.ret) {
						CPL=min(CPL,l.R[i]);
					}
					else {
						break;
					}
				}
				else {
					v.ret=min(v.ret,LV);
				}
			}
			else {
				if(V&(1<<i)) {
					if(RV<v.ret) {
						CPR=max(CPR,r.L[i]);
					}
					else {
						break;
					}
				}
				else {
					v.ret=min(v.ret,RV);
				}
			}
		}
		
		return v;
	};
	
	SegTree_1(){
		MINUS(tmp.L);
		MINUS(tmp.R);
		tmp.ret=1<<30;
		tmp.PL=tmp.PR=-1;
		val=vector<C>(NV*2,tmp);
	};
	C getval(int x,int y,int l=0,int r=NV,int k=1) { // x<=i<y
		if(r<=x || y<=l) return tmp;
		if(x<=l && r<=y) return val[k];
		return comp(getval(x,y,l,(l+r)/2,k*2),getval(x,y,(l+r)/2,r,k*2+1));
	}
	void update(int entry, int v) {
		
		entry += NV;
		int i;
		val[entry]=tmp;
		val[entry].PL=val[entry].PR=entry-NV;
		if(v>V) {
			val[entry].ret=A[entry-NV];
		}
		else {
			val[entry].ret=1<<30;
			FOR(i,30) if(v&(1<<i)) val[entry].L[i]=val[entry].R[i]=entry-NV;
		}
		
		while(entry>1) entry>>=1, val[entry]=comp(val[entry*2],val[entry*2+1]);
	}
};
SegTree_1<info,1<<18> st;
int Q;

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	tmp.ret=1<<30;
	FOR(i,30) tmp.L[i]=tmp.R[i]=-1;
	
	cin>>T;
	while(T--) {
		cin>>N>>V;
		FOR(i,N) {
			cin>>A[i];
			rmq.set(i,A[i]);
		}
		V--;
		x=1;
		while(x<N) x*=2;
		rmq.NV2=x;
		rmq.build();
		FOR(i,N) {
			cin>>B[i];
			st.update(i,B[i]);
		}
		cin>>Q;
		while(Q--) {
			int L,R;
			cin>>i>>L>>R;
			L--;
			if(i==1) {
				B[L]=R;
				st.update(L,B[L]);
			}
			else {
				auto r=st.getval(L,R);
				if(r.ret==1<<30) {
					cout<<-1<<" ";
				}
				else {
					cout<<r.ret<<" ";
				}
			}
		}
		cout<<endl;
		
	}
}

まとめ

SegTreeに色々乗せるものが多くて大変。