[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;
}
标签:ch,合唱队,int,左边,le,插入,HNOI2010,区间 来源: https://www.cnblogs.com/LAK666/p/16450125.html