[HNOI2010]合唱队

原题链接

题解

首先本题只从左边和右边取,所以剩下的必然是区间,妥妥的区间DP,直接设状态:

\(f[i][j][0]\) 表示第 \(i\) 人从左边插入区间 \(i+1,j\)

\(f[i][j][1]\) 表示第 \(j\) 人从右边插入区间 \(i,j-1\)

因为第 \(i\) 个人从左边插入,根据题意,他就会小于前面的人,而前面的人要么是第 \(i+1\) 从左插入,要么就是第 \(j\) 从右插入,所以这个状态的转移方程就是:

\[if(a[i]<a[j])f[i][j][0]+=f[i+1][j][1]; \]

\[if(a[i]<a[i+1])f[i][j][0]+=f[i+1][j][0]; \]

同理,第二种状态的转移方程可以顺势推出

\[if(a[j]>a[i])f[i][j][1]+=f[i][j-1][0]; \]

\[if(a[j]>a[j-1])f[i][j][1]+=f[i][j-1][1]; \]

最后的答案就是区间 \((1,n)\) 从左插入和从右插入的和,即 \(f[1][n][0]+f[1][n][1]\)

还剩最后一步,也就是边界值,如果 \(i=j\),那肯定只有一种情况,所以:
\(f[i][i][0]=f[i][i][1]=1(1 \le i \le n)\)

但这时我们发现,当 \(i=j\) 时,区间的答案为:左边插入 \(+\) 右边插入 \(=2\),但很明显答案为 \(1\),所以我们必须规定最开始只能从左插入,当然从右也行,所以更改后的边界条件为:

\[f[i][i][0]=1(1 \le i \le n) \]

注意

本题要取模 \(19650827\)

Code:

#include<bits/stdc++.h>
using namespace std;
void read(int &x)
{
	char ch=getchar();
	int r=0,w=1;
	while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar();
	while(isdigit(ch))r=(r<<3)+(r<<1)+(ch^48),ch=getchar();
	x=r*w;
}
const int N=1100,mod=19650827;
int f[N][N][2],a[N];
int main()
{
	int n;
	read(n);
	for(int i=1;i<=n;i++)
		read(a[i]);
	for(int i=1;i<=n;i++)f[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;
		if(a[i]<a[j])f[i][j][0]+=f[i+1][j][1];
		if(a[i]<a[i+1])f[i][j][0]+=f[i+1][j][0];
		if(a[j]>a[i])f[i][j][1]+=f[i][j-1][0];
		if(a[j]>a[j-1])f[i][j][1]+=f[i][j-1][1];
		f[i][j][0]%=mod;f[i][j][1]%=mod;
	}
	cout<<(f[1][n][0]+f[1][n][1])%mod;
	return 0;
}
posted @ 2022-07-06 11:26  Epoch_L  阅读(12)  评论(0编辑  收藏  举报