Codeforces 1178F Long Colorful Strip
题目链接
https://codeforces.com/contest/1178/problem/F1
https://codeforces.com/contest/1178/problem/F2
题目大意
给你一段一开始全是 $0$ 的数列,你需要操作 $n$ 次,每次将一段连续数字相同的区间变为 $i$,问有多少种方法可以使得最终结果为给定的数列。
题目分析
首先考虑 $F1$ 的每个数仅出现一次的情况。显然问一个区间的填法,肯定是需要先找到一个最小的数,然后从它开始枚举左右扩展多少来计算。显然可以很容易的区间 $DP$ 出来。
再来考虑 $F2$ 中每个数出现很多次的问题。首先所有连续的数并不影响结果,因此可以把连续的数先全部合并。
这时假如满足题目要求,那么剩余的数的数量不会超过 $2n$ 个。
此时可以处理出来每个数的基础区间,也就是说必须填某一个数的区间。与此同时可以处理给定数列是否可行。
然后依然使用区间 $DP$ ,只是要注意到每次都需要考虑选择一个区间。这道题就可以解决了。
注意如果枚举区间的时候出现一个比当前最小的数更小的数,那么不能再向外枚举区间。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5;
const int mod = 998244353;
int n, m;
int a[maxn];
int nl[1005], nr[1005];
vector <int> vec;
long long f[1005][1005];
struct ex {
int l, r;
};
ex p[1005];
long long dfs(int l, int r){
int i, j;
int x = 0, y;
long long L = 0, R = 0;
if(f[l][r] != -1)return f[l][r];
f[l][r] = 1;
if(l > r)return 1;
for(i=1;i<=n;i++){
if(l <= p[i].l and p[i].r <= r){
x = i;
break;
}
}
if(!x)return 1;
f[l][r] = dfs(p[x].l + 1, p[x].r - 1);
L = dfs(l, p[x].l - 1);
for(i=p[x].l-1;i>=l;){
j = nl[i];
if(j == 0x3f3f3f3f){
i--;
break;
}
L = (L + dfs(j, p[x].l - 1) * dfs(l, j - 1)) % mod;
i = j - 1;
}
R = dfs(p[x].r + 1, r);
for(i=p[x].r+1;i<=r;){
j = nr[i];
if(j == 0){
i++;
break;
}
R = (R + dfs(p[x].r + 1, j) * dfs(j + 1, r)) % mod;
i = j + 1;
}
f[l][r] = f[l][r] * L % mod * R % mod;
return f[l][r];
}
int main(){
int i, j;
int x, y;
scanf("%d%d", &n, &m);
for(i=1;i<=m;i++){
scanf("%d", &x);
if(!vec.size() or vec.back() != x){
vec.push_back(x);
}
}
m = 0;
for(i=1;i<=n;i++){
p[i].l = 0x3f3f3f3f;
p[i].r = 0;
}
for(auto x : vec){
a[++m] = x;
p[a[m]].l = min(p[a[m]].l, m);
p[a[m]].r = max(p[a[m]].r, m);
}
for(i=n;i>0;i--){
for(j=n;j>i;j--){
if(max(p[j].l, p[i].l) <= min(p[j].r, p[i].r) and !(p[i].l <= p[j].l and p[j].r <= p[i].r)){
printf("0\n");
return 0;
}else if(max(p[j].l, p[i].l) <= min(p[j].r, p[i].r)){
p[i].l = min(p[i].l, p[j].l);
p[i].r = max(p[i].r, p[j].r);
}
}
}
for(i=1;i<=n;i++){
for(j=p[i].l;j<=p[i].r;j++){
if(a[j] < i){
printf("0\n");
return 0;
}
}
}
memset(nl, 0x3f, sizeof(nl));
for(i=1;i<=n;i++){
nl[p[i].r] = min(nl[p[i].r], p[i].l);
nr[p[i].l] = max(nr[p[i].l], p[i].r);
}
memset(f, -1, sizeof(f));
dfs(1, m);
printf("%lld\n", f[1][m]);
return 0;
}