torus711 のアレ

主に競技プログラミングの問題について書きます.PC 以外だと数式が表示されないかもしれないです

AtCoder Beginner Contest 344, E : Insert or Erase

問題概要

 相異なる $n$ 項からなる数列 $A = \langle A_1, A_2, \dots, A_n \rangle$ がある.
 次の 2 種からなるクエリを $q$ 個,順に処理せよ.

  • $( 1, x, y )$ : $A$ に含まれる $x$ の直後に $y$ を挿入する($x$ の存在は保証される).
  • $( 2, x )$ : $A$ に含まれる $x$ を削除する($x$ の存在は保証される).

各クエリの処理後,$A$ は空でなく,要素は相異なる.
 最終的な $A$ を出力せよ.

制約

  • $1 \leq n, q \leq 2 \times 10^5$
  • $1 \leq A_i \leq 10^9$

解法

 ※実装が主題なので,C++ に寄った書き方になっています.他言語でも相当するライブラリをもっていると思うので,適宜読み替えてもらえると助かります.

 やりたいこと自体は問題文そのままという感じがあります.とりあえず,各時点での $A$ を保持しながら実際に挿入・削除を行う方向で考えてみます.
 前提として,$A$ の要素数の最大値が $\Theta( n + q )$ であることをリマークしておきます.
 普段列を扱うときは(わたしは)専ら std::vector を使っているわけですが,std::vector では任意位置での挿入・削除の最悪計算量が $\Theta( n + q )$ 時間になるので,これを $\Theta( q )$ 回やってしまうと TLE します.よって,他の方法を考える必要があります.
 競プロではやや影が薄い[要出典]ですが CS 分野では割と初期に習うデータ構造として,連結リストがあります.特に双方向リストは*1,対象の位置が分かっているとき,その位置での挿入・削除を $O( 1 )$ 時間で実行できます.わざわざ「対象の位置が分かっているとき」と言ったことから察せられるかもしれませんが,単純な実装では特定の要素の位置を知るのに最悪 $\Theta( n + q )$ 時間かかってしまいます.C++ では双方向リストは std::list として提供されていますが,要素の位置の特定に時間がかかるのは同様*2で,そのままでは使えません.逆に言えば,指定された要素の位置の特定さえ高速にできればよいので,この部分を別のデータ構造で補います.「要素から位置を知りたい」ということなので,単純に,要素毎にそれを保持するノードへのイテレータを保持する std::map を用意します.こうすれば,指定された要素のイテレータ(=位置)を $O( \log( n + q ) )$ 時間で知ることができるようになります.位置さえ分かってしまえば挿入・削除は $O( 1 )$ 時間なので,これを $q$ 回やっても入力受け取りや初期化と合わせて $O( n + q \log( n + q ) )$ 時間となります.

コード

#include <list>

int main()
{
	IN( int, N );
	VI A( N );
	cin >> A;

	list< int > ls( ALL( A ) );

	map< int, decltype( begin( ls ) ) > its;
	for ( auto it = begin( ls ); it != end( ls ); ++it )
	{
		its[ *it ] = it;
	}

	IN( int, Q );
	REP( Q )
	{
		IN( int, T );

		if ( T == 1 )
		{
			IN( int, X, Y );

			ls.insert( next( its[X] ), Y );
			its[Y] = next( its[X] );
		}
		else
		{
			IN( int, X );

			ls.erase( its[X] );
		}
	}

	VI res;
	copy( ALL( ls ), BI( res ) );
	cout << res << endl;

	return 0;
}

*1:対象位置のみ分かっていて predecessor を事前に知らなくてよいという意図で双方向リストに言及しています.

*2:と言うとやや不正確かもしれなくて,正確には,特定の要素を探す機能はコンテナ側では提供していなくて,std::find を使うことになるので線形時間になるという話です.