histcat

histcat

P3205 [HNOI2010] 合唱団

題目#

より良い演出効果を得るために、AAA 合唱隊の責任者である小 A は、合唱隊のメンバーを身長に基づいて並べる必要があります。合唱隊には合計で $n$ 人がいて、第 $i$ 人の身長は $h_i$ メートル($1000 \le h_i \le 2000$)であり、任意の 2 人の身長は異なることが知られています。最終的に並べられる隊形は $A$ 人が一列に立つことと仮定し、問題を簡略化するために、小 A は以下のような並び方を考えました:彼は全員を任意の順序で初期の隊形に立たせ、その後、左から右へ以下の原則に従って順次各人を最終的な隊形に挿入します:

  • 最初の人は空の現在の隊形に直接挿入します。
  • 2 人目以降の各人について、前の人よりも背が高い場合($h$ が大きい)、現在の隊形の最右に挿入します。前の人よりも背が低い場合($h$ が小さい)、現在の隊形の最左に挿入します。

$n$ 人全員が現在の隊形に挿入されると、最終的な隊形が得られます。

例えば、6 人が初期の隊形に立ち、身長がそれぞれ $1850, 1900, 1700, 1650, 1800, 1750$ の場合、小 A は以下の手順で最終的な隊形を得ます:

  • $1850$。
  • $1850, 1900$、なぜなら $1900 > 1850$。
  • $1700, 1850, 1900$、なぜなら $1700 < 1900$。
  • $1650, 1700, 1850, 1900$、なぜなら $1650 < 1700$。
  • $1650, 1700, 1850, 1900, 1800$、なぜなら $1800 > 1650$。
  • $1750, 1650, 1700, 1850, 1900, 1800$、なぜなら $1750 < 1800$。

したがって、最終的な隊形は $1750, 1650, 1700, 1850, 1900, 1800$ です。

小 A は理想的な隊形を心に描いており、どれだけの初期の隊形が理想的な隊形を得ることができるかを知りたいと思っています。

答えを $19650827$ で割った余りを求めてください。

全データの $100%$ に対して、$n \le 1000$、$1000 \le h_i \le 2000$ です。

問題解決#

なぜ区間 dp を使うのか?(問題解決から引用)

区間 dp にはある性質があります —— 大きな区間は小さな区間を含む、この問題はその性質に合致しています:

image

まず、f[i][j] を理想的な区間 i から j に対する方案数と定義します。dp の遷移は最後の異なる部分に依存します、つまり最後の数が左側に挿入されたのか右側に挿入されたのかですが、遷移が難しいことがわかります。

そこで、f[0/1][i][j] を理想的な区間 i から j に対する方案数(0 は最後の数が左側に挿入されたことを示し、1 はその逆)と定義し、分類して遷移を行います。

コード#

#include<bits/stdc++.h>

using namespace std;
const int N = 1010;
const int mod = 19650827;
int n, a[N];

int read()
{
	int f = 1, x = 0;
	char ch = getchar();
	while(ch < '0' || ch > '9')
	{
		if(ch == '-') f = -1;
		ch = getchar();
	}

	while(ch >= '0' && ch <= '9')
	{
		x = x * 10 + ch - '0';
		ch = getchar();
	}
	return f * x;
}

int f[N][N][2];

int main()
{
	n = read();
	for(int i = 1;i <= n;i++)
	{
		a[i] = read();
	}

	for(int i = 1;i <= n;i++)
	{
		f[i][i][0] = 1;
	}

	for(int L = 2;L <= n;L++)
	{
		for(int i = 1;i + L - 1 <= n;i++)
		{
			int j = i + L - 1;
		
			if(a[i + 1] > a[i])
				f[i][j][0] += f[i + 1][j][0];
			if(a[j] > a[i])
				f[i][j][0] += f[i + 1][j][1];
			if(a[i] < a[j])
				f[i][j][1] += f[i][j - 1][0];
			if(a[j - 1] < a[j])
				f[i][j][1] += f[i][j - 1][1];
			f[i][j][0] %= mod, f[i][j][1] %= mod;
		}
	}

	cout << (f[1][n][0] + f[1][n][1]) % mod;

	return 0;
}
読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。