P3205 [HNOI2010]合唱队 题解

CSDN同步

原题链接

简要题意:

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

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

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

\(n \leq 1000 , a_i \leq 2000\).

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

\(f_{i,j}\) 表示 \([i,j]\) 区间的答案,但是我们需要知道 前一个元素的值,因此可以令 \(f_{i,j,0}\) 表示 \([i,j]\) 区间,当前最后一次操作在 \(i\)\(f_{i,j,1}\) 则最后一次操作在 \(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\) 成立则为 \(1\),否则为 \(0\),可以免去条件判断。

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

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

实际得分:\(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;
}

posted @ 2020-07-18 10:35  bifanwen  阅读(127)  评论(0编辑  收藏  举报