P3205 [HNOI2010]合唱队 题解

博客园同步

原题链接

简要题意:

有长度为 n n n 的序列 a a a 和一个空序列 b b b. 下面进行操作:

  • a 1 a_1 a1 直接进入 b b b 序列中。
  • 对于 a i = 2 n a_{i=2}^n ai=2n 的每个 a i a_i ai,若 a i > a i + 1 a_i > a_{i+1} ai>ai+1,则将 a i a_i ai 插入 b b b 序列末尾;否则插入 b b b 序列开头。

每个 a a a 唯一对应一个 b b b,但是每个 b b b 并不唯一对应一个 a a a.现在已知 b b b 序列,试求满足条件的 a a a 序列的个数。答案对 19260817 19260817 19260817 取模。

n ≤ 1000 , a i ≤ 2000 n \leq 1000 , a_i \leq 2000 n1000,ai2000.

考虑区间 dp \text{dp} dp.

f i , j f_{i,j} fi,j 表示 [ i , j ] [i,j] [i,j] 区间的答案,但是我们需要知道 前一个元素的值,因此可以令 f i , j , 0 f_{i,j,0} fi,j,0 表示 [ i , j ] [i,j] [i,j] 区间,当前最后一次操作在 i i i f i , j , 1 f_{i,j,1} fi,j,1 则最后一次操作在 j j j.

不难得出:

{ f i , j , 0 = f i , j , 1 = 1                                                        , i = j f i , j , 0 = f i + 1 , j , 0 ∗ ( a i < a i + 1 ) + f i + 1 , j , 1 ∗ ( a i < a j ) , i ≠ j f i , j , 1 = f i , j − 1 , 0 ∗ ( a j > a i ) + f i , j − 1 , 1 ∗ ( a j > a j − 1 ) , i ≠ j \begin{cases} f_{i,j,0} = f_{i,j,1} = 1 \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space,i=j \\ f_{i,j,0} = f_{i+1,j,0} * (a_i < a_{i+1}) + f_{i+1,j,1} * (a_i < a_j) , i \not = j\\ f_{i,j,1} = f_{i,j-1,0} * (a_j > a_i) + f_{i,j-1,1} * (a_j > a_{j-1}) , i \not = j\\ \end{cases} fi,j,0=fi,j,1=1                                                      ,i=jfi,j,0=fi+1,j,0(ai<ai+1)+fi+1,j,1(ai<aj),i=jfi,j,1=fi,j1,0(aj>ai)+fi,j1,1(aj>aj1),i=j

其中 ( A ) (A) (A) 表示 A A A 成立则为 1 1 1,否则为 0 0 0,可以免去条件判断。

本质实在考虑当前从 i + 1 → i i+1 \rightarrow i i+1i 还是 j ↔ i j \leftrightarrow i ji 还是 j − 1 → j j-1 \rightarrow j j1j,不难发现。

时间复杂度: O ( n 2 ) \mathcal{O}(n^2) O(n2).

实际得分: 100 p t s 100pts 100pts.

#include<bits/stdc++.h>
using namespace std;

int n,a[1001];
int dp[1001][1001][2]; //不小心写成了 dp,见谅

const int p=19650827;

inline int read(){char ch=getchar();while(ch<'0' || ch>'9') ch=getchar();
	int x=0;while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();return x;}

int main(){
	n=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<=n;i++) dp[i][i][0]=1; //初始化
	for(int len=2;len<=n;len++)
	for(int i=1;i<=n-len+1;i++){
		int j=i+len-1;
		dp[i][j][0]=(dp[i+1][j][0]*(a[i]<a[i+1])+dp[i+1][j][1]*(a[i]<a[j]))%p;
		dp[i][j][1]=(dp[i][j-1][0]*(a[j]>a[i])+dp[i][j-1][1]*(a[j]>a[j-1]))%p;
	} //区间 dp 套路
	printf("%d\n",(dp[1][n][0]+dp[1][n][1])%p); //注意取模
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值