1 条题解

  • 0
    @ 2026-7-22 21:17:12

    看完题,注意到 n5000n \leq 5000。加上求方案数,大概率就是 dp 了。

    一开始想的是 fi,j,kf_{i,j,k} 表示前 ii 个数,第 ii 个数是 jj,且已经连续了 kk 个数的方案数。但是这样就是 O(n3)O(n^3) 的状态了,会炸掉。

    于是我们考虑保留其中最重要的两维,即 iijj,并且考虑枚举 kk,因为正常情况下,时间复杂度会比较好优化。

    fi,jf_{i,j} 表示前 ii 个数,第 ii 个数是 jj 的方案数。转移如下。

    $$f_{i,j} = \sum_{k=i-j \wedge [\forall p \in [k,i),p = j \vee p = -1]}^{i-1}\sum_{col \not = j} f_{k,col}$$

    这样子我们就可以考虑前缀和优化了。首先预处理 kk 的可行范围,这个可以 O(n2)O(n^2) 预处理。

    考虑记 si,js_{i,j} 表示 k=1icoljfi,j\sum_{k=1}^i \sum_{col \not = j} f_{i,j}。这样子就可以 O(n2)O(n^2) 的转移了。

    初始化因为没有数字,为了防止影响后面转移,我们设 00 号位置数字是 00。这样子 f0,0=1,s0,col=1(col[1,n])f_{0,0} = 1,s_{0,col} = 1(col \in [1,n])

    #include <bits/stdc++.h>
    using namespace std;
    #define il inline
    #define N 5005
    il int rd(){
    	int s = 0, w = 1;
    	char ch = getchar();
    	for (;ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') w = -1;
    	for (;ch >= '0' && ch <= '9'; ch = getchar()) s = ((s << 1) + (s << 3) + ch - '0');
    	return s * w;
    }const int P = 998244353;
    int n, a[N], pre[N][N], f[N][N], sum[N], s[N][N];
    int main(){
    	n = rd();
    	for (int i = 1; i <= n; i++){
    		a[i] = rd();
    		for (int j = 1; j <= n; j++) if (a[i] == j || a[i] == -1) pre[i][j] = pre[i - 1][j] + 1;
    	}f[0][0] = sum[0] = 1;
    	for (int j = 1; j <= n; j++) s[0][j] = 1;
    	for (int i = 1; i <= n; i++){
    		for (int j = 1; j <= n; j++) if (a[i] == j || a[i] == -1){
    			int lst = i - min(pre[i][j], j);
    			f[i][j] = (s[i - 1][j] - (lst ? s[lst - 1][j] : 0) + P) % P, sum[i] = (sum[i] + f[i][j]) % P;
    		}for (int j = 1; j <= n; j++) s[i][j] = (s[i - 1][j] + (sum[i] - f[i][j] + P) % P) % P;
    	}printf ("%lld\n", sum[n]);
    	return 0;
    }
    
    
    • 1

    信息

    ID
    11517
    时间
    2000ms
    内存
    1024MiB
    难度
    10
    标签
    递交数
    3
    已通过
    1
    上传者