[LOJ2273][JXOI2017]数列 DP

不难发现一个性质,若对于 Ai1 A i − 1 的限制是 Li1Ai1Ri1 L i − 1 ≤ A i − 1 ≤ R i − 1 ,对于 Ai A i 的限制是 LiAiRi L i ≤ A i ≤ R i ,那么有 Li1Li L i − 1 ≤ L i Ri1Ri R i − 1 ≥ R i ,于是我们考虑DP。
fi,x,l,r f i , x , l , r 表示填了 i i 个数,Ai x x A1..i1 x ≤ x 的最大值是 l l x的最小值是 r r (注意l,r不是限制 Ai A i 的范围)的方案数。
对于 x,l,r x , l , r 互不相等的状态, Ai1 A i − 1 必然充当了 l l 或者r,转移就是 fi,x,l,rklfi1,l,k,r+krfi1,r,l,k f i , x , l , r ← ∑ k ≤ l f i − 1 , l , k , r + ∑ k ≥ r f i − 1 , r , l , k
对于 x,l,r x , l , r 只有两者相等的状态显然是不存在的。
对于 x,l,r x , l , r 全相等的状态需要特殊处理,因为 Ai1 A i − 1 不一定充当了 l l r,可能是之前一个 =x = x 的数充当了两者,所以讨论两种情况:
1. Ai1=x A i − 1 = x ,这时 fx,x,xjxkxfi1,x,j,k f x , x , x ← ∑ j ≤ x ∑ k ≥ x f i − 1 , x , j , k
2. Ai1x A i − 1 ≠ x ,这时 fx,x,xyx(kyfi1,y,k,x+kyfi1,y,x,k) f x , x , x ← ∑ y ≠ x ( ∑ k ≤ y f i − 1 , y , k , x + ∑ k ≥ y f i − 1 , y , x , k )
然后用前缀和维护一些 kx,kx ∑ k ≤ x , ∑ k ≥ x 什么的就可以把复杂度降到 O(nmax3{ai}) O ( n max 3 { a i } ) 了。
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define ui unsigned int 
using namespace std;
const int mod=998244353;
ui n,r[55],mxr,f[155][155][155],sl[155][155][155],sr[155][155][155];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&r[i]);
        mxr=max(mxr,r[i]+1);
    }
    for(int i=0;i<=mxr;i++)
        sl[0][i][mxr]=sr[0][0][i]=1;
    for(int i=1;i<=n;i++)
    {
        memset(f,0,sizeof(f));
        for(int ai=1;ai<=r[i];ai++)
        {
            for(int L=0;L<ai;L++)
                for(int R=ai+1;R<=mxr;R++)
                    f[ai][L][R]=(sl[L][L][R]+sr[R][L][R])%mod;          
            for(int R=ai;R<=mxr;R++)
                f[ai][ai][ai]=(f[ai][ai][ai]+sl[ai][mxr][R])%mod;
            for(int ai_1=0;ai_1<=mxr;ai_1++)
                if(ai_1!=ai)
                    f[ai][ai][ai]=(f[ai][ai][ai]+sl[ai_1][ai_1][ai]+sr[ai_1][ai][ai_1])%mod;
        }           
        memset(sl,0,sizeof(sl));            
        for(int ai=1;ai<=r[i];ai++)
            for(int L=0;L<=mxr;L++)
                for(int R=ai;R<=mxr;R++)    
                    sl[ai][L][R]=(f[ai][L][R]+(L==0?0:sl[ai][L-1][R]))%mod;         
        memset(sr,0,sizeof(sr));            
        for(int ai=1;ai<=r[i];ai++)
            for(int L=0;L<=ai;L++)
                for(int R=mxr;R>=0;R--) 
                    sr[ai][L][R]=(f[ai][L][R]+sr[ai][L][R+1])%mod;
    }
    ui ans=0;
    for(int ai=1;ai<=r[n];ai++)
        for(int L=0;L<=ai;L++)
            for(int R=ai;R<=mxr;R++)
                ans=(ans+f[ai][L][R])%mod;
    printf("%d",ans);               
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值