区间dp+预处理——cf1278F(难题)

感觉很难的区间dp,主要是状态难想

/*
对于一个区间[i,j],设其最小的颜色编号是c=Min[i,j],那么该区间显然有一大段是以c为底的
设这个颜色在该区间出现位置的两端是L[c],R[c],那么我们枚举该区间以c为底的颜色段[l,r]
显然l<=L[c],r>=R[c],则该区间被分为了五段:[i,l-1],[l,L[c]-1],[L[c],R[c]],[R[c]+1,r],[r+1,j]
1,5 段是不以c为底色的段, 2,4 是以c为底,但被其他颜色覆盖的段, 3 是被c包围,中间还有其他颜色的段

需要预处理Min[i,j],nxt[i]
枚举区间[i,j],再枚举l:[i,L[c]-1],r:[R[c]+1,j],最后处理中间的[L[c],R[c]],即遍历所有被c隔开的段落
*/
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mod 998244353
#define N 1007
#define M 2000005
inline ll fk(ll x){return x>=mod?x-mod:x;}

ll n,m,a[M],b[M],tot;
ll L[N],R[N],Min[N][N],f[N][N];
int pos[N],nxt[N];

int main(){
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        cin>>a[i];
    }
    for(int i=1;i<=m;i++)
        if(a[i]!=a[i-1])b[++tot]=a[i];
    if(tot>2*n){cout<<'0'<<'
';return 0;}

    m=tot;for(int i=1;i<=m;i++)a[i]=b[i];

    //处理序列自动机
    for(int i=0;i<=m;i++)pos[i]=m+1;
    for(int i=m;i>=1;i--){
        nxt[i]=pos[a[i]];
        pos[a[i]]=i;
    }

    //处理L,R数组
    memset(L,0x3f,sizeof L);
    for(ll i=1;i<=m;i++){
        L[a[i]]=min(L[a[i]],i);
        R[a[i]]=max(R[a[i]],i);
    }

    //处理Min数组
    memset(Min,0x3f,sizeof Min);
    for(int i=1;i<=m;i++)
        for(int j=i;j<=m;j++)
            for(int k=i;k<=j;k++)
                Min[i][j]=min(Min[i][j],a[k]);

    //初始化
    for(int i=0;i<=m+1;i++){
        if(i && i<=m && L[a[i]]==i && R[a[i]]==i)f[i][i]=1;//没被隔断的区间
        for(int j=0;j<=m+1;j++)
            if(i>j)f[i][j]=1;
    }

    for(int k=1;k<m;k++)
        for(int i=1;i+k<=m;i++){
            int p=Min[i][i+k];if(p>N)continue;
            if(L[p]<i || R[p]>i+k)continue;//p的范围超过了[i,i+k],说明这个区间不用计算

            ll cntl=0,cntr=0,sum=1;
            for(int j=i;j<=L[p];j++)//枚举[i,L[p]]
                cntl=fk(cntl+f[i][j-1]*f[j][L[p]-1]%mod);

            for(int j=R[p];j<=i+k;j++)//枚举[R[p],j]
                cntr=fk(cntr+f[R[p]+1][j]*f[j+1][i+k]%mod);

            for(int j=L[p];j<R[p];j=nxt[j])//枚举[L[p],R[p]]中被p隔开的所有段
                sum=sum*f[j+1][nxt[j]-1]%mod;
            f[i][i+k]=cntl*cntr%mod*sum%mod;
        }

    cout<<f[1][m]<<'
';
}
原文地址:https://www.cnblogs.com/zsben991126/p/11634431.html