「解题报告」 [JXOI2017]数列 (DP)
题意
给定一个长度为 (n) 的整数数列 ({r}), 构造数列 ({a}), 满足以下条件
- (1 le a_i le r_i)
- 对于 (i ge 3), 设 (L) 为 (a_{1sim i-2}) 中 小于等于 (a_{i-1}) 的最大值 (当 (a_{1sim i-2}) 中不存在小于等于 (a_{i-1}) 的值时, (L=-inf) ), (R) 为 (a_{1sim i-2}) 中 大于等于 (a_{i-1}) 的最小值 (当 (a_{1sim i-2}) 中不存在大于等于 (a_{i-1}) 的值时, (R=inf) ), 满足 $ L le a_i le R$.
求满足上述条件的数列 ({a}) 的数量.
数据范围
$ n le 50, r_i le 150$
思路
计数题, 考虑 (DP).
设 (f[i][L][R]) 为 : 考虑到 (a_i), (L) 为 (a_{1 sim i-1}) 中小于等于 (a_i) 的最大值, (R) 为 (a_{1 sim i-1}) 中大于等于 (a_i) 的最小值时, 数列 ({a}) 的数量.
首先明确一点, 当 (L ot=R) 时, (a_i) 需满足 (a_i ot = L) 且 (a_i ot =R). 因为若 (a_i=L) 且 (L ot=R), 那么大于等于 (a_i) 的最小值就不是 (R) 而是 (L) 了, 不符合 (DP) 状态的意义; 当 (a_i=R) 时同理. 这一点在等下转移的时候会用到.
为了便于处理, 我们把 (L=-inf) 的情况和 (R=inf) 的情况分别设为 (L=0) 和 (R=max{r_i}+1).
枚举 (L',R') 进行转移, 三种情况分类讨论.
-
先考虑边界情况, 当 (L'=R'=L) 时, 表示 (a_{i+1}) 的取值为 (L), 那么 (L') 和 (R') 就与 (a_{i}) 无关了, 那么它可以取 ((L,min(r_i,R)) 中的任一值, 转移系数为 (min(r_i,R-1)-L). 当 (L'=R'=R) 时同理.
-
而当 (L'=R') 并且 (L' ot=L), (L' ot= R) 时, 就表示 (a_{i+1}) 的取值与 (a_{i}) 一致, 即 (L'=R'=a_i), 枚举 (a_i) 的所有可能取值转移即可, 系数为 (1).
-
当 (L' ot=R') 时, (L') 和 (R') 中必有一个等于 (a_i), 我们仍是枚举 (a_i) 所有可能的取值转移即可, 系数为 (1).
最后统计答案的计算式如下.
其中 (R-1-L) 表示的是 (a_n) 的所有可能取值.
代码
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int _=50+7;
const int __=150+7;
const ll mod=998244353;
int n,lim[_],inf;
ll f[_][__][__],ans;
void _pls(ll &x,ll y){ x=(x+y)%mod; }
int main(){
#ifndef ONLINE_JUDGE
freopen("x.in","r",stdin);
#endif
cin>>n;
if(n==1){ scanf("%d",&lim[1]); printf("%d
",lim[1]); return 0; }
for(int i=1;i<=n;i++){ scanf("%d",&lim[i]); inf=max(inf,lim[i]); }
inf++;
for(int i=1;i<=lim[1];i++){
if(i<lim[2]) f[2][i][inf]=1;
f[2][0][i]=1;
if(i<=lim[2]) f[2][i][i]=1;
}
for(int i=2;i<=n;i++)
for(int l=0;l<=lim[i];l++)
for(int r=l;r<=inf;r++){
if(l==r){
if(l<=min(lim[i],lim[i+1])) _pls(f[i+1][l][l],f[i][l][l]);
continue;
}
for(int k=l+1;k<=min(r-1,lim[i]);k++){
if(k<=r-2) _pls(f[i+1][k][r],f[i][l][r]);
_pls(f[i+1][l][k],f[i][l][r]);
if(k<=lim[i+1]) _pls(f[i+1][k][k],f[i][l][r]);
}
if(l&&l<=lim[i+1]) _pls(f[i+1][l][l],f[i][l][r]*(ll)(min(lim[i],r-1)-l)%mod);
if(r&&r<=lim[i+1]) _pls(f[i+1][r][r],f[i][l][r]*(ll)(min(lim[i],r-1)-l)%mod);
}
for(int l=0;l<=lim[n];l++)
for(int r=0;r<=inf;r++)
if(l&&l==r) _pls(ans,f[n][l][r]);
else _pls(ans,f[n][l][r]*(ll)(min(lim[n],r-1)-l)%mod);
printf("%lld
",ans);
return 0;
}