P3205 [HNOI2010]合唱队 题解

博客园同步

原题链接

简要题意:

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

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

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

n 1000 , a i 2000 n \leq 1000 , a_i \leq 2000 .

考虑区间 dp \text{dp} .

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

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

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

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

实际得分: 100 p t s 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;
}

猜你喜欢

转载自blog.csdn.net/bifanwen/article/details/107425020
今日推荐