0%

10809102 NOIP模拟题

C (CF1178F Short Colorful Strip)

给定全 \(0\) 序列,第 \(i\) 次操作可以把值相同的区间的值赋值为 \(i\),给定目标排列,求出操作方案数

有两个很明显的性质,首先,第 \(i\) 次操作区间一定包括值为 \(i\) 的位置,其次,当一个区间被操作后,这个区间与其他区间相互独立

后者是一个很好的性质,意味着对于每个区间只需要关注最小合法位置,枚举区间划分即可,最后区间相乘即可 所以,考场上写了一个 \(O(n^4)\)\(DP\)

\(f_{p,l,r}\) 为区间 \([l,\ r]\)\(p\) 次操作的操作方案数,\(g_{l,r,k}\) 为区间 \([l,\ r]\) 的不严格后继,不存在是为 \(0\)\(q_i\) 为值为 \(i\) 的位置,则有

\[ f_{p,\ l,\ r}\ =\ \sum_{i=l}^{q_p}\sum_{j=q_p}^rf_{g_{l,i-1,p+1},l,i-1}f_{g_{i,j,p+1},i,j}f_{g_{j+1,r,p+1},j+1,r}[min_{i\le k\le r}\lbrace s_k\rbrace\ge p] \]

边界 \(f_{0,l,r}=1\)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <cstdio>
#include <algorithm>
#include <cctype>
using namespace std;
typedef long long int64;
inline int read(int f = 1, int x = 0, char ch = ' ')
{
while(!isdigit(ch = getchar())) if(ch == '-') f = -1;
while(isdigit(ch)) x = x*10+ch-'0', ch = getchar();
return f*x;
}
const int N = 1e2+5, P = 998244353;
int n, s[N], q[N];
int64 f[N][N][N];
int g[N][N][N];
int64 dp(int p, int l, int r)
{
int64 &ret = f[p][l][r];
if(ret) return ret;
if(p == 0) ret = 1;
else
{
for(int i = q[p]; i >= l&&p <= s[i]; --i)
for(int j = q[p]; j <= r&&p <= s[j]; ++j)
ret = (ret+dp(g[l][i-1][p+1], l, i-1)*dp(g[i][j][p+1], i, j)%P*dp(g[j+1][r][p+1], j+1, r)%P)%P;
}
return ret;
}
int main()
{
n = read();
for(int i = 1; i <= n; ++i) s[i] = read(), q[s[i]] = i;
for(int k = 1; k <= n; ++k)
for(int l = 1; l <= n; ++l)
for(int r = l; r <= n; ++r)
if(s[r] >= k) g[l][r][k] = min(s[r], g[l][r-1][k]?g[l][r-1][k]:n);
else g[l][r][k] = g[l][r-1][k];
printf("%lld\n", dp(1, 1, n));
return 0;
}

期望 \(70\)

实际上我们可以省略第一维状态,因为不管怎么样,\([l,\ r]\) 填的数一定是最小值,这样一个区间就确定了唯一一个对应的次序

\(f_{l,r}\)\([l,\ r]\) 的方案数,\(g_{l,r}\)\([l,\ r]\) 的最小值,不妨设为 \(x\)

则有

\[ f_{l,r}\ =\ \sum_{i=l}^{q_p}\sum_{j=q_p}^rf_{l,i-1}f_{i,q_p-1}f_{q_p+1,j}f_{j+1,r} \]

\(l\ge r\)\(f_{l,r}=1\)

观察上式,\(i\)\(j\) 的枚举相互独立,可以优化为 \(O(n^3)\)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <cstdio>
#include <algorithm>
#include <cctype>
using namespace std;
typedef long long int64;
inline int read(int f = 1, int x = 0, char ch = ' ')
{
while(!isdigit(ch = getchar())) if(ch == '-') f = -1;
while(isdigit(ch)) x = x*10+ch-'0', ch = getchar();
return f*x;
}
const int N = 5e2+5, P = 998244353;
int n, m, p[N], q[N];
int64 f[N][N];
int g[N][N];
int64 dp(int l, int r)
{
int64 &ret = f[l][r];
if(ret) return ret;
if(l >= r) ret = 1;
else
{
int x = g[l][r];
int64 fl = 0, fr = 0;
for(int i = l; i <= q[x]; ++i) fl = (fl+dp(l, i-1)*dp(i, q[x]-1)%P)%P;
for(int i = q[x]; i <= r; ++i) fr = (fr+dp(q[x]+1, i)*dp(i+1, r)%P)%P;
ret = fl*fr%P;
}
return ret;
}
int main()
{
n = read(), m = read();
for(int i = 1; i <= n; ++i) p[i] = read(), q[p[i]] = i;
for(int l = 1; l <= n; ++l)
for(int r = l; r <= n; ++r)
g[l][r] = min(p[r], g[l][r-1]?g[l][r-1]:n);
printf("%lld\n", dp(1, n));
return 0;
}