博客园同步
原题链接
简要题意:
有长度为
n 的序列
a 和一个空序列
b. 下面进行操作:
-
a1 直接进入
b 序列中。
- 对于
ai=2n 的每个
ai,若
ai>ai+1,则将
ai 插入
b 序列末尾;否则插入
b 序列开头。
每个
a 唯一对应一个
b,但是每个
b 并不唯一对应一个
a.现在已知
b 序列,试求满足条件的
a 序列的个数。答案对
19260817 取模。
n≤1000,ai≤2000.
考虑区间
dp.
用
fi,j 表示
[i,j] 区间的答案,但是我们需要知道 前一个元素的值,因此可以令
fi,j,0 表示
[i,j] 区间,当前最后一次操作在
i;
fi,j,1 则最后一次操作在
j.
不难得出:
⎩⎪⎨⎪⎧fi,j,0=fi,j,1=1 ,i=jfi,j,0=fi+1,j,0∗(ai<ai+1)+fi+1,j,1∗(ai<aj),i=jfi,j,1=fi,j−1,0∗(aj>ai)+fi,j−1,1∗(aj>aj−1),i=j
其中
(A) 表示
A 成立则为
1,否则为
0,可以免去条件判断。
本质实在考虑当前从
i+1→i 还是
j↔i 还是
j−1→j,不难发现。
时间复杂度:
O(n2).
实际得分:
100pts.
#include<bits/stdc++.h>
using namespace std;
int n,a[1001];
int dp[1001][1001][2];
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;
}
printf("%d\n",(dp[1][n][0]+dp[1][n][1])%p);
return 0;
}