ARC127E Priority Queue

题面


容易发现加入的数是一个排列,但是考虑最后的情况不好做。我们反过来看,枚举最终集合 s s s,判断它是否合法。

那么操作就要反过来:第一个操作从加入任意一个数变成删去任意一个数,第二次操作从删去最大的数变成加入一个最大的数。当然顺序也要反过来。

把当前未出现的数看作空位,假设当前是操作二,如果没有空位就不合法。显然对于第一个操作一定是删去最大的数最优(说明若当前集合有加入的数,一定要把它们先删去,再删原本有的数)。

于是我们现在把操作正着看,发现每个操作二都可以与前面的一个操作一抵消,剩下的操作一就是答案集合,而且它们随时间加入单调递增。

现在我们就可以用 DP 求出最终集合 s s s 的方案数了。设 f i , j f_{i,j} fi,j 表示 s s s 中第 i i i 位填的是 [ 1 , j ] [1,j] [1,j] 范围的数的答案。因为还要保证合法,所以再设 a i a_i ai 表示 s s s 中第 i i i 位最大能填多少。显然 a i a_i ai 是好求的。

下面转移 f i , j f_{i,j} fi,j。如果第 i i i 位不填,方案是 f i , j − 1 f_{i,j-1} fi,j1;如果第 i i i 位填 j j j,方案是 f i − 1 , j − 1 f_{i-1,j-1} fi1,j1(注意 j ≤ a i j\le a_i jai)。于是得出转移:
f i , j = f i , j − 1 + f i − 1 , j − 1 f_{i,j}=f_{i,j-1}+f_{i-1,j-1} fi,j=fi,j1+fi1,j1

于是就做完了。时间复杂度 O ( a b ) O(ab) O(ab)

#include
using namespace std;
#define ll long long
const ll mod=998244353;
int n,m,a[10001],p[10001],vis[12],cnt,cnt1;
ll f[5001][5001];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n+m;i++){
        cin>>a[++cnt];
        if(a[cnt]==1){
            a[cnt]=++cnt1;
        }
        else{
            cnt-=2;
        }
    }
    for(int i=0;i<=n;i++) f[0][i]=1;
    for(int i=1;i<=n-m;i++){
        for(int j=1;j<=a[i];j++){
            f[i][j]=(f[i][j-1]+f[i-1][j-1])%mod;
        }
        for(int j=a[i]+1;j<=n;j++) f[i][j]=f[i][a[i]];
    }
    cout<<f[n-m][a[n-m]];
}

你可能感兴趣的:(c++)