情報妖精の競プロ日記

AtCoderの問題に対する方針を主に書きます

第5回 ドワンゴからの挑戦状 予選

第5回 ドワンゴからの挑戦状 予選に参加しました!
第5回 ドワンゴからの挑戦状 予選 旧版
第5回 ドワンゴからの挑戦状 予選 beta版

解けた問題だけ解説をしていきますー

要項

第5回 ドワンゴからの挑戦状 予選
rated 2800
11/24(土) 20:00 ~ 22:00 (120分)
200-400-600-800(500)-1000 (5問)
ペナルティ: 5分

方針

ABCdの3完1部分点を高速に、多分予選は通過できないけどパフォーマンスはかなり良くなるはず

A - Thumbnail

まず、平均点を求めます。
この時、平均点を求めると小数点以下の値で誤差を発生させる可能性があるため、あえて平均点のN倍を求めます。
次に各a_iについてNa_iと平均点のN倍との差を求め、差が最小のものを選びます。
計算量は{\rm O}(N)です。

#include<iostream>
using namespace std;
int main() {
	int N, a[100];
	cin >> N;
	int sum = 0; // 平均点のN倍は総和に等しい
	for (int i = 0;i < N;++ i) {
		cin >> a[i];
		sum += a[i];
		a[i] *= N; // N倍することで誤差を消す!
	}
	int diff = abs(sum - a[0]), ans = 0;
	for (int i = 0;i < N;++ i) {
		if (diff > abs(sum - a[i])) { // もし、より差が小さいものを見つけたら更新
			ans = i;
			diff = abs(sum - a[i]);
		}
	}
	cout << ans;
	return 0;
}

B - Sum AND Subarrays

まず、連続した数列の和……は累積和なので予めS_i=\sum_{j=1}^i a_jを計算しておきましょう。
累積和が計算されているなら\frac{N(N+1)}{2}個の部分列を列挙するのも{\rm O}(N^2)でできます。
次に、ビットごとの論理積を計算するという事は、少なくともK個の部分列で同一のビットが立っていないと立たせることができません。
逆に、そのようなビットでかつ最も高い位置にあるビットは立たせた方が良いです。
よって、一番上のビット(N \max(a_i) \leq 10^{12} \leq 2^{40})から順にK個のビットが立つか貪欲に見ていき、立たせることができるならそれを立たせると良いです。
その時、ついでにそのビットが立たないもの全てを0埋めしておくとバグを防ぐこともできます。
計算量は{\rm O}(N^2 \log(N\max(a_i))です。

C - k-DMC

制約よりQがかなり小さいので、{\rm O}(NQ)を考えます。
各クエリについて独立っぽいので、1回のクエリについては{\rm O}(N)で解く必要があるようです。
まず、カウンターとしてd = 0, m = 0, c = 0を定義します。
そして左から順にみていき、'D'が出てきたらdを1増やします。
次に、'M'が出てきたら、今まで'D'が出てきた個数分だけmを増やします。すなわちm += iですね。
次に、'C'が出てきたらcmだけ増やします。
これは、mというのは'D'と'C'に囲まれた'M'の個数(複数の'D'と共有して挟まれたならその個数だけ掛けたもの)を指すので、これがそのまま答えに足すべき個数だからです。
また、c-a < kの制約があるので、最後にk個前に'D'が出てきたならk個前から今の地点までの'M'の個数分だけmを減らします。
これは、予め'M'について出現個数を累積和で求めておけば{\rm O}(1)で求めることができますね。
以上の操作により、左から順に全ての操作を{\rm O}(1)で求められたので、各クエリについて{\rm O}(N)、よって計算量は{\rm O}(NQ)となりました。

結果

ooox- 3完(1200点) 17:36(3WA)-49:57(4WA)-89:08(1WA) / 129:08
289位 パフォーマンス: 1819
レート: 16421661(+19)

感想

ちょっと64bit整数関連でバグらせたけど、まぁ妥当な感じではないでしょうか。
C問題は問題としては面白く、時間もかかる問題だったので発想ができて解けたのは良かった範囲だと思います。
ただもう少し早解きしたかったなぁ……。