解题报告:luogu P3205
题目链接:P3205 [HNOI2010]合唱队
跟风切题
shy txdy!
手残点了一下标签,看到标签之后就秒了。
区间 \(dp\),注意这是整区间不完全利用性的。
什么意思呢?就是对于继承的区间,里面的元素不一定完全有用。
还记得 ABC163 的 \(E\) 题吗?(不记得可以看这里-->快戳我
有相似之处。
一般有用的是两端,中点或若干个显而易见的位置。
我们考虑设 \(dp_{i,j,0/1}\)为\([i,j]\)之中最后选左端点/右端点的方案数,显然:
\[dp_{i,j,0}=dp_{i+1,j,0}×[a_{i}<a_{i+1}]+dp_{i+1,j,1}×[a_i<a_j]
\]
\[dp_{i,j,1}=dp_{i,j-1,0}×[a_{j}>a_{i}]+dp_{i,j-1,1}×[a_j>a_{j-1}]
\]
最后的得数即为 \(dp_{1,n,0}+dp_{1,n,1}\)。
\(Code\):
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define read(x) scanf("%d",&x)
#define MOD 19650827
#define ll long long
int n,a[1005];
ll dp[1005][1005][3];
int main()
{
read(n);
for(int i=1;i<=n;i++) read(a[i]);
for(int i=1;i<=n;i++) dp[i][i][0]=dp[i][i][1]=1ll;
for(int i=1;i<=n-1;i++)
{
if(a[i]<a[i+1]) dp[i][i+1][0]=dp[i][i+1][1]=1ll;
else dp[i][i+1][0]=dp[i][i+1][0]=0;
}
for(int i=3;i<=n;i++)
{
for(int l=1;l+i-1<=n;l++)
{
int r=l+i-1;
if(a[l+1]>a[l]) dp[l][r][0]+=dp[l+1][r][0]%MOD;
if(a[r]>a[l]) dp[l][r][0]+=dp[l+1][r][1]%MOD;
if(a[l]<a[r]) dp[l][r][1]+=dp[l][r-1][0]%MOD;
if(a[r-1]<a[r]) dp[l][r][1]+=dp[l][r-1][1]%MOD;
}
}
printf("%lld\n",(dp[1][n][0]+dp[1][n][1])%MOD);
return 0;
}