kmjp's blog

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

AtCoder ARC #080 : E - Young Maids

E手間取るしF解けないし微妙な出来。
http://arc080.contest.atcoder.jp/tasks/arc080_c

問題

1~Nの順列である数列Pがある。ここから別の数列Qを構築することを考える。
ここから連続する2要素(a,b)を選び、Pから抜き出してQの先頭に(x,y)の順で追加する。
こうして構築できるQのうち辞書順最小のものを求めよ。

解法

Pの部分列P[L...R]に関し、そこから上記手段で構築できる辞書順最小のものを考える。
辞書順最小となるには、最後に残る2要素(P[a],P[b])が最小となるようにすればよい。
そのためにはそれまでにそれ以外の要素を消す必要がある。

よってP[L...(a-1)],P[(a+1)...(b-1)],P[(b+1)...R]がいずれも偶数長でなければならない(空でもよい)。
辞書順最小な値を選ぶことから、まず条件を満たすaを求めよう。
それはL≦a<Rで(a-L)が偶数なもののうちP[a]が最小のものである。
aが決まると同様にbも求めることが出来、a≦b<RでP(b-a)が奇数なもののうちP[b]が最小のものである。

a,bはindexが偶奇縛りがある中で値の最小値を求めることになる。
よって、indexの偶奇別に2つRMQのデータ構造を作っておけばよい。

さて、P[L...R]から(P[a],P[b])を先に取り除くと、以後はP[L...(a-1)],P[(a+1)...(b-1)],P[(b+1)...R]の3つについて処理を行うことになる。
各数列はどの順で処理を行ってもよい。
よって以下のようにする。

処理対象の部分列P[L...R]に対し、それぞれ取り除く要素の候補(P[a],P[b])を求め、set等で最小値を管理する。
複数の部分列が残っているとき、最小値を取り除ける部分列P[L'...R']を選び、対応する(P[a'],P[b'])を解の末尾に追加しよう。
その後、P[L'...(a'-1)],P[(a'+1)...(b'-1)],P[(b'+1)...R']の3つの部分列に対し、それぞれ対応する取り除く要素の候補を求めてsetに放り込めばよい。

int N;
int A[202020];
int rev[202020];

template<class V,int NV> class SegTree_1 {
public:
	vector<V> val;
	static V const def=303030;
	V comp(V l,V r){ return min(l,r);};
	
	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, V v) {
		entry += NV;
		val[entry]=v;
		while(entry>1) entry>>=1, val[entry]=comp(val[entry*2],val[entry*2+1]);
	}
};

SegTree_1<int,1<<20> ev,od;
set<vector<int>> cand;

void add(int L,int R) {
	vector<int> ret;
	if(R<=L) return;
	
	int tar;
	if(L%2==0) tar=ev.getval(L,R);
	else tar=od.getval(L,R);
	int x=rev[tar];
	ret.push_back(tar);
	
	if((x+1)%2==0) tar=ev.getval(x+1,R);
	else tar=od.getval(x+1,R);
	int y=rev[tar];
	ret.push_back(tar);
	
	ret.push_back(L);
	ret.push_back(x);
	ret.push_back(y);
	ret.push_back(R);
	cand.insert(ret);
}

void dodo(vector<int> V) {
}

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	
	cin>>N;
	FOR(i,N) {
		cin>>A[i];
		if(i%2==0) {
			ev.update(i,A[i]);
			od.update(i,1<<20);
		}
		else {
			od.update(i,A[i]);
			ev.update(i,1<<20);
		}
		rev[A[i]]=i;
	}
	
	vector<int> Q;
	
	add(0,N);
	while(cand.size()) {
		vector<int> v=*cand.begin();
		cand.erase(cand.begin());
		Q.push_back(v[0]);
		Q.push_back(v[1]);
		add(v[2],v[3]);
		add(v[3]+1,v[4]);
		add(v[4]+1,v[5]);
	}
	
	
	FOR(i,N) _P("%d%c",Q[i],(i==N-1)?'\n':' ');
	
}

まとめ

1000ptでもよさそうと思ったけど盛りすぎ?