以下の内容はhttps://torus711.hatenablog.com/entry/2025/05/18/182928より取得しました。


AtCoder Beginner Contest 406, C : ~

問題概要

 サイズ $n$ の順列 $P = \langle P_1, P_2, \dots, P_n \rangle$ が与えられる.$P$ の連続部分列 $A$ であって,以下の条件を(全て)満たすものの個数を求めよ:

  • $|A| \geq 4$
  • $A_1 < A_2$
  • $\exists! i \in [ 2, |A| - 1 ] \cap \mathbb Z, A_{ i - 1 } < A_i > A_{ i + 1 }$
  • $\exists! i \in [ 2, |A| - 1 ] \cap \mathbb Z, A_{ i - 1 } > A_i < A_{ i + 1 }$

制約

  • $4 \leq n \leq 3 \times 10^5$

準備

 列 $A$ の $l$ 項目から $r$ 項目までを取り出してできる連続部分列を $A[ l, r ]$ と書くことにします.
 

解法

 どういう列を数えたいか噛み砕くと,

  • 増加・減少が切り替わる箇所がちょうど $2$ 箇所.

となります.増加から減少に切り替わる位置を $x$, 減少から増加に切り替わる位置を $y$ とすると,

  • $A_1 < A_2$ より $x < y$.
  • $A[ 1, x ]$ は単調増加.
  • $A[ x, y ]$ は単調減少.
  • $A[ y, |A| ]$ は単調増加.

です.
 条件を満たすある部分列 $A$ において $x, y$ として取られる可能性がある添字は $P$ においても増加・減少が切り替わる位置なので,$P$ を走査して連続する $3$ 要素の大小関係を見ることで事前に列挙できます.このように列挙した添字(を昇順に並べたもの)を $I$ とすると,各 $i \in [ 1, |I| - 1 ] \cap \mathbb Z$ について,$P[ I_i, I_{ i + 1 } ]$ は,$P$ の単調増加な連続部分列であって極大なものか,$P$ の単調減少な連続部分列であって極大なもののいずれかです.$P[ I_i, I_{ i + 1 } ]$ が単調減少となるような $i \in [ 1, |I| - 1 ] \cap \mathbb Z$ について,$P[ I_i, I_{ i + 1 } ]$ の左右から単調増加列を伸ばすことで得られる列が,数えたい列です.
 $P[ I_i, I_{ i + 1 } ]$ が単調減少だとします.この左側に単調増加列を伸ばす(伸ばす方向と増加の方向が逆なことに注意)と考えると,最大限伸ばしたときの左端は減少から増加に切り替わる位置か,$P$ の先頭のいずれかなので,$i > 1$ ならば $I_{ i - 1 }$ で,そうでなければ $1$ です.同様に右側についても,$i < |I| - 1$ ならば $I_{ i + 2 }$ で,そうでなければ $n$ です.このままだと場合分けが煩雑ですが,$P$ の先頭に $n + 1$ を,末尾に $0$ を追加して得られる列 $\langle n + 1, P_1, P_2, \dots, P_n, 0 \rangle$ に対して上記のことを考えると,$i > 1$ と $i < |I| - 1$ が保証されるので場合分けを消せます.すると,左に伸ばす左端の候補は $I_{ i - 1 }, I_{ i - 1 } + 1, \dots, I_i - 1$ の $I_i - I_{ i - 1 }$ 通りで,右に伸ばす右端の候補は $I_{ i + 1 } + 1, I_{ i + 1 } + 2, \dots, I_{ i + 2 }$ の $I_{ i + 2 } - I_{ i + 1 }$ 通りとなるので,この $i$ に対して条件を満たす部分列の個数は,
\[
( I_i - I_{ i - 1 } ) \times ( I_{ i + 2 } - I_{ i + 1 } )
\]
となります.後は,$P[ I_i, I_{ i + 1 } ]$ が単調減少であるような全ての $i$ について上記の値を計算して和をとることで答えが求まります.
 このアルゴリズムは,番兵の挿入と $I$ の構築に $\Theta( n )$ 時間($P$ を std::vector でもつ場合)かかり,答えの計算に $O( n )$ 時間かかります.従って全体では $\Theta( n )$ 時間であり正答できます.

コード

int main()
{
	IN( int, N );
	VI P( N );
	cin >> P;
	MAP_PRED( P );
	P.insert( begin( P ), N );
	P.PB( -1 );

	VI indices;
	REP( i, 1, N + 1 )
	{
		if ( ( P[ i - 1 ] < P[i] ) != ( P[i] < P[ i + 1 ] ) )
		{
			indices.PB( i );
		}
	}

	LL res = 0;
	for ( int i = 1; i + 2 < SZ( indices ); i += 2 )
	{
		res += LL( indices[i] - indices[ i - 1 ] ) * ( indices[ i + 2 ] - indices[ i + 1 ] );
	}
	cout << res << endl;

	return 0;
}



以上の内容はhttps://torus711.hatenablog.com/entry/2025/05/18/182928より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14