「解题报告」 [JXOI2017]数列 (DP)

「解题报告」 [JXOI2017]数列 (DP)

题意

给定一个长度为 \(n\) 的整数数列 \(\{r\}\), 构造数列 \(\{a\}\), 满足以下条件

  • \(1 \le a_i \le r_i\)
  • 对于 \(i \ge 3\), 设 \(L\)\(a_{1\sim i-2}\)小于等于 \(a_{i-1}\) 的最大值 (当 \(a_{1\sim i-2}\) 中不存在小于等于 \(a_{i-1}\) 的值时, \(L=-inf\) ), \(R\)\(a_{1\sim i-2}\)大于等于 \(a_{i-1}\) 的最小值 (当 \(a_{1\sim 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\not=R\) 时, \(a_i\) 需满足 \(a_i\not = L\)\(a_i\not =R\). 因为若 \(a_i=L\)\(L\not=R\), 那么大于等于 \(a_i\) 的最小值就不是 \(R\) 而是 \(L\) 了, 不符合 \(DP\) 状态的意义; 当 \(a_i=R\) 时同理. 这一点在等下转移的时候会用到.

为了便于处理, 我们把 \(L=-inf\) 的情况和 \(R=inf\) 的情况分别设为 \(L=0\)\(R=max\{r_i\}+1\).

枚举 \(L',R'\) 进行转移, 三种情况分类讨论.

  1. 先考虑边界情况, 当 \(L'=R'=L\) 时, 表示 \(a_{i+1}\) 的取值为 \(L\), 那么 \(L'\)\(R'\) 就与 \(a_{i}\) 无关了, 那么它可以取 \((L,min(r_i,R-1)]\) 中的任一值, 转移系数为 \(min(r_i,R-1)-L\). 当 \(L'=R'=R\) 时同理.

  2. 而当 \(L'=R'\) 并且 \(L'\not=L\), \(L'\not= R\) 时, 就表示 \(a_{i+1}\) 的取值与 \(a_{i}\) 一致, 即 \(L'=R'=a_i\), 枚举 \(a_i\) 的所有可能取值转移即可, 系数为 \(1\).

  3. \(L'\not=R'\) 时, \(L'\)\(R'\) 中必有一个等于 \(a_i\), 我们仍是枚举 \(a_i\) 所有可能的取值转移即可, 系数为 \(1\).

最后统计答案的计算式如下.

\[ans=\sum_{0\le L < R \le max\{r_i\}+1} f[n][L][R]*(R-1-L) + \sum_{1\le k \le r_n} f[n][k][k] \]

其中 \(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\n",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\n",ans);
    return 0;
}
posted @ 2020-06-07 22:14  BruceW  阅读(189)  评论(0编辑  收藏  举报