zoukankan      html  css  js  c++  java
  • 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
    ",(f1[1][n]+f2[1][n])%M);
    	return 0;
    }
    
  • 相关阅读:
    关于JDK和JRE的一些总结
    Jackson 格式化日期问题
    CentOS6.8安装mysql5.6
    CentOS6.8安装JDK1.7
    VMware NAT方式 CentOS 6.8配置静态IP
    CentOS6.8使用源码安装Git
    关于SourceTree License
    记录平时遇到的问题
    使用React-Router遇到的那些坑
    移动端响应式布局好文收集
  • 原文地址:https://www.cnblogs.com/xcg123/p/13925977.html
Copyright © 2011-2022 走看看