Official

C - Robot Takahashi Editorial by mechanicalpenciI


子供と大人をあわせて体重の軽い方から順に左から右へ一列に並べます。 (すなわち、体重によってソートを行います。)
このとき高橋君の判定する方法は、 隣り合う \(2\) 人であって体重が異なるような \(2\) 人の間、 もしくは一番左の人の左または一番右の人に右に線を引き、 その線より左の人を子供、右の人を大人と判定すると言い換えることができます。 ここで、体重が等しい \(2\) 人を高橋君は区別できないため、その間には線を引けない事に注意してください。

すべての線の引き方に対して正しく判定できる人数を愚直に求めようとすると時間計算量が \(\Theta(N^2)\) だけかかってしまい、今回の制約 (\(N\)は最大 \(2\times 10^5\)) の下では間に合いません。

そこで、最初に線を一番左に引いた状態を考え、少しずつ右に動かしていく事を考えます。
まず、最初の時点では全員を大人と判定している事になるため、大人の人数が高橋君が正しく判定できる人数です。

次に、左から \(i\) 番目の人のすぐ左にあった線をその人のすぐ右に動かしたときに正しく判定できる人数がどう変わるか考えてみましょう。 ここで、左から \(i\) 番目の人以外に対しては高橋君の判定は変わらないため、その部分について正しく判定できる人数は変化しません。 変化するのは左から \(i\) 番目の人に対する判定のみであり、その人を大人と判定していたものが子供と判定するように変化します。 そのため、もし左から \(i\) 番目の人が子供であれば、正しく判定できる人数は \(1\) 人増え、 もし左から \(i\) 番目の人が大人であれば、正しく判定できる人数は \(1\) 人減ります。

これを繰り返し、条件をみたす(すなわち線を挟む \(2\) 人の体重が異なる, あるいは線が一番左または一番右にある)すべてのタイミングにおいて、 正しく判定できる人数を確認し、その最大値を取れば答えは求まります。

体重によって人をソートするのに \(O(N\log N)\)、 最初に大人の人数を数えるのに \(O(N)\)、 線が \(1\) 人またぐたびに計算し直すのに \(O(1)\) であるため左端から右端まですべて順に計算するのに \(O(N)\) であるのであわせて、 時間計算量は \(O(N\log N)\) となり、十分間に合います。

なお、体重によってソートするときには c++ であれば std::pair 等を用いたり、python であれば \(2\) 次元の list を適切な条件でソートする事で 体重の数値と大人か子供かの情報をセットにしてソートをすることができます。

c++ による実装例 :

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

int main() {
	int n, x, ans;
	string s;
	vector<pair<int, char> >a;
	cin >> n;
	cin >> s;
	ans = 0;
	for (int i = 0; i < n; i++) {
		cin >> x;
		a.push_back({ x,s[i] });
		if (s[i] == '1')ans++;
	}
	sort(a.begin(), a.end());
	x = ans;
	for (int i = 0; i < n; i++) {
		if (a[i].second == '1')x--;
		else x++;
		if (i < (n - 1)) {
			if (a[i].first != a[i + 1].first)ans = max(ans, x);
		}
		else ans = max(ans, x);
	}
	cout << ans << endl;
	return 0;
}

posted:
last update: