[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 A i x x A1..i1 A 1.. i − 1 x ≤ x 的最大值是 l l x ≥ x 的最小值是 r r (注意 l,r l , r 不是限制 Ai A i 的范围)的方案数。
对于 x,l,r x , l , r 互不相等的状态, Ai1 A i − 1 必然充当了 l l 或者 r 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 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
#include
#include
#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;Lfor(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;
}

你可能感兴趣的:(dp,前缀和与差分)