Official

M - お片付け/Put Away Editorial by kyopro_friends


この問題は次のようなクエリ問題だと思うことができます。

数列 \(X\) が与えられる。次の \(2\) 種類のクエリを処理せよ:

  • \(x\) が与えられる。\(X_i\geq x\) となる最小の \(i\) を求めよ
  • \(i,x\) が与えられる。\(X_i\)\(X_i-x\) に置き換えよ

ここで、1番目のクエリは次と同値になります。

  • \(x\) が与えられる。\(\max_{j\leq i}X_j\)\(x\) 以上となる最小の \(i\) を求めよ

このように読み替えることで、「\(\max_{j\leq i}X_j\)\(x\) 以上か?」は \(i\) に関して単調になり、二分探索をすることができるようになります。したがって、これらのクエリは、区間maxを取得できるセグメントツリーを用いることで高速に処理できます。

元の問題を解く際の計算量はセグメントツリー上の二分探索の実装の仕方に依存し、\(O(N\log M)\) または \(O(N(\log M)^2)\) となります。

実装例(C++ with ACL)

#include<bits/stdc++.h>
#include<atcoder/segtree>
using namespace std;

using S=int;
S op(S x,S y){return max(x,y);}
S e(){return 0;}

int main(){
	int n,m;
	cin >> n >> m;
	vector<int>nimotsu(n),hako(m);
	for(int i=0;i<n;i++)cin >> nimotsu[i];
	for(int i=0;i<m;i++)cin >> hako[i];
	
	atcoder::segtree<S,op,e>seg(hako);
	for(int i=0;i<n;i++){
		auto f=[&](S x){return x<nimotsu[i];};
		int r=seg.max_right<decltype(f)>(0,f);
		if(r==m){
			cout << "No\n" << i+1 << endl;
			return 0;
		}
		seg.set(r,seg.get(r)-nimotsu[i]);
	}
	
	cout << "Yes" << endl;
	for(int i=0;i<m;i++)cout << hako[i]-seg.get(i) << " ";
}

posted:
last update: