P3205 [HNOI2010]合唱队

P3205 [HNOI2010]合唱队

题目描述

为了在即将到来的晚会上有更好的演出效果,作为 \(AAA\) 合唱队负责人的小 \(A\) 需要将合唱队的人根据他们的身高排出一个队形。假定合唱队一共 \(n\) 个人,第 \(i\) 个人的身高为 \(h_i\)​ 米 \((1000 \le h_i \le 2000)\),并已知任何两个人的身高都不同。假定最终排出的队形是 \(A\) 个人站成一排,为了简化问题,小 \(A\) 想出了如下排队的方式:他让所有的人先按任意顺序站成一个初始队形,然后从左到右按以下原则依次将每个人插入最终棑排出的队形中:

第一个人直接插入空的当前队形中。

对从第二个人开始的每个人,如果他比前面那个人高(\(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\) 取模的值。

输入格式

第一行一个整数 \(n\)
第二行 \(n\) 个整数,表示小 \(A\) 心中的理想队形。

输出格式

输出一行一个整数,表示答案 \(\mod 19650827\) 的值。

输入输出样例

输入 #1

4
1701 1702 1703 1704

输出 #1

8

说明/提示

对于 \(30\%\) 的数据,\(n \le 100\)
对于 \(100\%\) 的数据,\(n \le 1000\)\(1000 \le h_i \le 2000\)

Solution

分析题目性质我们可以发现,随着越来越多的人加入到合唱队形中,\(A\) 的理想队形是逐渐向两端扩增的。
假如说我们已经排好了 \([l,r]\) 的队形,再加入一个人后的理想队形我们就排好了 \([l-1,r]\)\([l,r+1]\),像这样,每一步操作都使我们的目标区间发生有规律的变化,我们应该用区间\(dp\)来解决。
因为题目中有在左边插入在右边插入两种情况,所以我们开两个数组来分别记录两种情况。

状态设置:

\(f1[l][r]\) 我们已经排好了 \([l,r]\) 的理想队形,且最后一个人插在了最左边的方案数。\(f2[l][r]\) 为插在了最右边的方案数。

状态转移:

发现当前这个人是插在最左边和最右边取决于他与上个人的身高差,而上个人又分为最左边和最右边两种情况,所以我们要分四种情况讨论:
\(1.\) 当前的人比上一个人矮,上一个人被插在了最左边。故当前这个人需要插到最左边,即 \(l\) 的位置,且上一个人是 \(a[l+1]\),所以有 \(f1[l][r]+=f1[l+1][r](a[l]<a[l+1])\)
\(2.\) 当前的人比上一个人矮,上一个人被插在了最右边。故当前这个人需要插在最左边,即 \(l\) 的位置,且上一个人是 \(a[r]\),所以有 \(f1[l][r]+=f2[l+1][r](a[l]<a[r])\)
\(3.\) 当前的人比上一个人高,上一个人被插在了最左边。故当前这个人需要插在最右边,即 \(r\) 的位置,且上一个人是 \(a[l]\),所以有 \(f2[l][r]+=f1[l][r-1](a[r]>a[l])\)
\(4.\) 当前的人比上一个人高,上一个人被插在了最右边。故当前这个人需要插在最右边,即 \(r\) 的位置,且上一个人是 \(a[r-1]\),所以有 \(f2[l][r]+=f2[l][r-1](a[r]>a[r-1])\)

边界条件

排好一个人的方案数为 \(1\),但注意我们不能让 \(f1[i][i]\)\(f2[i][i]\) 同时为 \(1\),因为你不能认为他既是从左边插入也是从右边插入,这样会算重。

答案输出

排完 \([1,n]\) 的理想队列,最后一个人插在左边和插在右边的方案数之和。
\(f1[1][n]+f2[1][n]\)

\(Code\)

#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
inline ll read()
{
	char ch=getchar();
	ll a=0,x=1;
	while(ch<'0'||ch>'9') 
	{
		if(ch=='-') x=-x;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		a=(a<<1)+(a<<3)+(ch^48);
		ch=getchar();
	}
	return a*x;
}
const ll N=1005;
const ll M=19650827;
ll n;
int a[N];
ll f1[N][N],f2[N][N];
int main()
{
	n=read();
	for(ll i=1;i<=n;i++) a[i]=read();
	for(ll i=0;i<=n;i++) f2[i][i]=1;
	for(ll len=2;len<=n;len++)
	{
		for(ll l=1;l+len-1<=n;l++)
		{
			ll r=l+len-1;
			if(a[l]<a[l+1]) f1[l][r]+=f1[l+1][r];
			if(a[l]<a[r])   f1[l][r]+=f2[l+1][r];
			if(a[r]>a[l])   f2[l][r]+=f1[l][r-1];
			if(a[r]>a[r-1]) f2[l][r]+=f2[l][r-1];
			f1[l][r]%=M;f2[l][r]%=M;
		}
	}
	printf("%lld\n",(f1[1][n]+f2[1][n])%M);
	return 0;
}
posted @ 2020-11-04 14:57  暗い之殇  阅读(123)  评论(0编辑  收藏  举报