kmjp's blog

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

yukicoder : No.3122 Median of Medians of Division

これは考察パートが難しい。
https://yukicoder.me/problems/no/3122

問題

整数列Aに対し、以下のクエリに順次答えよ。

  • Aの1要素を指定された値を更新する。
  • Aの部分列A[L,R]が指定される。Aをいくつかの連続部分列に分割し、それぞれの中央値からなる数列の中央値を取るときの最大値を求めよ。

解法

解xを二分探索することを考えると、x未満の値をできるだけまとめて連続部分列にすると、2回目の中央値を取るときに有利である。
ここから取れる値を考察すると、求める値は結局以下となる。

  • A[L], A[R]及び連続する2要素のminを集めた数列のうち、小さい値から2つ目

あとはSegTreeを用い、隣接する2要素のminの集合に対し、小さい方から2番目までの値を管理すれば、クエリに高速に求められる。

static pair<int,int> const def={-1,-1};

template<class V,int NV> class SegTree_1 {
public:
	vector<V> val;
	
	V comp(V l,V r){
		int A[4]={l.first,l.second,r.first,r.second};
		sort(A,A+4);
		return {A[3],A[2]};
	};
	
	SegTree_1(){val=vector<V>(NV*2,def);};
	V getval(int x,int y,int l=0,int r=NV,int k=1) { // x<=i<y
		if(r<=x || y<=l) return def;
		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;
		val[entry]={v,-1};
		while(entry>1) entry>>=1, val[entry]=comp(val[entry*2],val[entry*2+1]);
	}
};

SegTree_1<pair<int,int>,1<<20> st;

int N,Q;
int A[202020];

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>N>>Q;
	FOR(i,N) cin>>A[i];
	FOR(i,N-1) st.update(i,min(A[i],A[i+1]));
	while(Q--) {
		cin>>i>>x>>y;
		if(i==1) {
			x--;
			A[x]=y;
			if(x) st.update(x-1,min(A[x-1],A[x]));
			if(x<N-1) st.update(x,min(A[x],A[x+1]));
		}
		else {
			x--,y--;
			auto p=st.getval(x,y);
			int B[4]={A[x],A[y],p.first,p.second};
			sort(B,B+4);
			cout<<B[2]<<endl;
		}
	}
}

まとめ

これ次の問題より考察難しくないか…?