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
",(f1[1][n]+f2[1][n])%M);
return 0;
}