CF 2208E Counting Cute Arrays 题解
Link
写一篇简单的题解。
OP1
考虑如何从 \(A\) 数组变到 \(X\) 数组,其实可以直接使用单调栈。
操作是如果目前 \(X_i\) 在栈顶,直接将它抛出,然后把 \(i\) 压入单调栈,否则一直弹栈。
那么首先的不合法的情况是存在 \(j<i\),满足 \(X_j<X_i<j<i\),即在 \(j\) 这个点,\(X_i\) 就已经被弹出了。
OP2
现在如果 \(X_i=i-1\) 或者 \(X_i=-1\) 时怎么做,那么我们的 \(X_i\) 的弹出要么弹出若干次,要么只能弹出一次。
现在设 \(dp_{i,j}\) 表示目前考虑到 \(i\) 这个点,现在单调栈的大小为 \(j\)。
那么考虑转移:
\[dp_{i,j}=dp_{i-1,j-1}+\sum\limits_{k=j-1}^i dp_{i-1,k}
\]
好的,那么现在所有的前置就完了。
OP3
现在我们把一个 \(X_i \ne -1\) 的 \(i\) 单独拎出来看。
发现 \((X_i,i)\) 中的所有点不可能成为 \(j>i\) 的 \(X_j\) 出现(因为 \(i \leq X_j\))。
也就是说这一段是独立的。而在 OP1 中,我们发现区间要么包含,要么就不交。那么当我们从小区间(长度较小的)往大区间考虑时,小区间对于大区间没有任何影响。
现在我们假设有 \([L,R]\) 这个区间,然后区间中包含了 \([l,r]\) 这段区间,相当于我们在 \((l,r]\) 这段区间填充 \(i-1\),因为遇到这个元素后,不会删除任何东西,而是加入 \(r\) 这个点,而 \([L,R]/[l,r]\) 这段一定为 \(-1\)。
那么现在我们将这个问题严格转换为了子问题,就可以递归处理了。
OP4
说一嘴实现,对于这种题,其实不用写递归,只需要搞一个指针看下一个没被考虑的点是哪一个,然后从长度小的往长度大的转移即可。
代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int N = 5005;
const int Md = 998244353;
int a[N], nxt[N], n;
ll ans = 0;
void calc(int l, int r) {
vector<ll> dp, sum;
dp.push_back(1), sum.push_back(1);
while (true) {
if (nxt[l] == 0) {
if (++l > r) break;
dp = sum;
dp.insert(dp.begin(), 0);
}
else {
if ((l = nxt[l]) > r)
break;
dp.insert(dp.begin(), 0);
}
sum = vector<ll>(dp.size());
sum.back() = dp.back();
for (int i = sum.size() - 2; ~i; --i)
sum[i] = (sum[i + 1] + dp[i]) % Md;
}
ans = (1ll * ans * sum[0]) % Md;
}
void clear(void) {
for (int i = 0; i <= n; ++i) nxt[i] = 0;
}
void solve(void) {
clear();
cin >> n;
ans = 1;
for (int i = 1; i <= n; ++i) cin >> a[i];
for (int i = 1; i <= n; ++i)
if (a[i] >= i) {
cout << 0 << '\n';
return;
}
for (int i = 1; i <= n; ++i)
for (int j = i + 1; j <= n; ++j)
if (a[j] != -1 && a[i] != -1 && a[i] < a[j] && a[j] < i) {
cout << 0 << '\n';
return;
}
vector<int> vec;
for (int i = 1; i <= n; ++i)
if (a[i] != -1) vec.push_back(i);
sort(vec.begin(), vec.end(), [&](int x, int y) {return x - a[x] < y - a[y]; });
for (int pos : vec) {
calc(a[pos], pos - 1);
nxt[a[pos]] = pos;
}
calc(0, n);
cout << ans << '\n';
}
int main() {
FASTIO;
int t;
cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号