kmjp's blog

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

Codeforces #1015 : F. Skyscape

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

問題

1~NのPermutation A、Bが与えられる。
Bの一部の要素は未定である。

Aに対し以下の処理をN回行ったとき、Bと一致させられるようにしたい。
条件を満たすBが存在するなら1例を示せ。

  • Aの部分列A[L...R]を指定すると、その中の最小値が部分列の先頭に移動する。

解法

条件を満たすA,Bは、i<jかつA[i]<A[j]の場合、Bにおいても値A[i]とA[j]の位置関係は値A[i]が手前に来ることである。
これを踏まえて、不定値となる各値がBにおいて範囲を求めたうえで、貪欲に値を小さい順に不定値の箇所に入れていこう。

int T;
int N;
int A[505050],B[505050],C[505050],reA[505050],reB[505050];
vector<int> block[505050];

template<class V,int NV> class SegTree_ma {
public:
	vector<V> val;
	static V const def=-3;
	V comp(V l,V r){ return max(l,r);};
	
	SegTree_ma(){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]);
	}
};

template<class V,int NV> class SegTree_mi {
public:
	vector<V> val;
	static V const def=1<<20;
	V comp(V l,V r){ return min(l,r);};
	
	SegTree_mi(){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_ma<int,1<<20> stma;
SegTree_mi<int,1<<20> stmi;
vector<pair<int,int>> ev[505050];
void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>T;
	while(T--) {
		cin>>N;
		FOR(i,N+2) {
			stma.update(i,-1);
			reA[i]=reB[i]=-1;
			stmi.update(i,N);
			ev[i].clear();
		}
		
		FOR(i,N) {
			cin>>A[i];
			reA[A[i]]=i;
		}
		FOR(i,N) {
			cin>>B[i];
			if(B[i]) {
				reB[B[i]]=i;
			}
		}
		//Bが逆転しないか確認
		for(i=1;i<=N;i++) if(reB[i]>=0) {
			//元々自身より小さい値が、前から後ろに移動した
			if(stma.getval(0,reA[i])>reB[i]) break;
			stma.update(reA[i],reB[i]);
		}
		if(i<=N) {
			cout<<"-1"<<endl;
			continue;
		}
		
		//行先未確定の要素の左端
		FOR(i,N+1) stma.update(i,-1);
		FOR(i,N) {
			if(reB[A[i]]>=0) {
				stma.update(A[i],reB[A[i]]);
			}
			else {
				//より小さい要素の移動先の右
				C[i]=stma.getval(0,A[i])+1;
			}
		}
		//行先未確定の要素の右端
		for(i=N-1;i>=0;i--) {
			if(reB[A[i]]>=0) {
				stmi.update(A[i],reB[A[i]]);
			}
			else {
				//より小さい要素の移動先の右
				x=stmi.getval(A[i],N+1)-1;
				if(x<C[i]) break;
				//C[i]~x
				ev[C[i]].push_back({-x,-A[i]});
			}
		}
		if(i>=0) {
			cout<<"-1"<<endl;
			continue;
		}
		priority_queue<pair<int,int>> Q;
		FOR(i,N) {
			FORR(v,ev[i]) Q.push(v);
			if(B[i]==0) {
				if(Q.empty()) break;
				if(-Q.top().first<i) break;
				B[i]=-Q.top().second;
				Q.pop();
			}
		}
		if(i<N) {
			cout<<"-1"<<endl;
			continue;
		}
		FOR(i,N) cout<<B[i]<<" ";
		cout<<endl;
		
	}
		
}

まとめ

ざっくり書くと短いんだけど、この範囲を求めるのが結構めんどい。