zoukankan      html  css  js  c++  java
  • 「解题报告」 [JXOI2017]数列 (DP)

    「解题报告」 [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') 进行转移, 三种情况分类讨论.

    1. 先考虑边界情况, 当 (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) 时同理.

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

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

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

    [ans=sum_{0le L < R le max{r_i}+1} f[n][L][R]*(R-1-L) + sum_{1le 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
    ",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;
    }
    
  • 相关阅读:
    全球视角商讨Linux的将来生长三趋势
    Firefox 3.0新版应战IE欣赏器 年夜战在即
    讲解SQL与Oracle外键束厄狭窄中的级联删除
    在Oracle中添加用户 赋权 修正暗码 解锁
    一个完好的Oracle rman备份规复参考示例
    Novell即将面临FSF起诉 终了其发行Linux
    Linux之x登录前后的转变
    Ubuntu Linux 8.04 Vsftp 虚构用户设置
    mysql修复坏表的步履办法
    用UTL_INADDR包获取曾经衔接用户的IP地址
  • 原文地址:https://www.cnblogs.com/BruceW/p/13062592.html
Copyright © 2011-2022 走看看