CF1761F Anti-median (Easy Version)
称一个排列是好的,当且仅当对于所有 \(m\) 都满足所有长度为 \(2m+1\) 的子串的中位数不在第 \(m+1\) 个。给定一个一些数被替换成 \(-1\) 的排列 \(p\),你需要统计所有可能的好排列的数量。答案对 \(10^9+7\) 取模。
\(n \leq 10^3\)。
先考察合法排列的形态。
对于长为 \(3\) 的子区间 \([p_1, p_2, p_3]\),必然有 \(p_2 > p_1, p_3\) 或 \(p_2 < p_1, p_3\),因此排列呈波浪形。
对于长为 \(5\) 的子区间 \([p_1,p_2,p_3,p_4,p_5]\),不妨设 \(p_3 > p_2,p_4\),那么 \(p_3\) 必须至少大于 \(p_1\) 和 \(p_5\) 中的一个数。因此,若排列奇数位为峰,则仅考虑奇数位时不能存在谷。因此奇数位必然先增后减。
同理,此时偶数位必然先减后增。
对于更长子区间的限制,假设中间的数为峰,则根据上述性质,必然存在一侧所有数全部小于它,且另一侧与它相邻的数也小于它,因此一定合法。
综上,我们得到了合法排列的充要条件。接下来考虑如何计数。
直接 DP 看起来没有什么前途,考虑继续寻找性质。不妨设奇数位置为峰,偶数位置为谷,则 \(n\) 在奇数位置且向两端单调递减,\(1\) 在偶数位置且向两端单调递增。
注意到在两端处一定有 \(p_1 > p_2\),\(p_{n - [2\mid n]} > p_{n - [2\nmid n]}\),所以从数值 \(n\) 开始,按 \(n ^ {-1} \to \cdots \to 3 \to 1 \to 2 \to 4 \to \cdots \to 1 ^ {-1}\) 走到数值 \(1\),数值单调递减。从右侧走同理。
这启发我们将偶数位置从左到右排列,再将奇数位置从右往左接在后面,首尾相连形成一个环。容易知道数值 \([1, k](1 \leq k \leq n)\) 在环上一定形成一段子区间。我们的计算方式是:先确定 \(1\) 的位置,然后每次考虑当前的数放在左侧还是右侧,在此过程中保证所有长为 \(3\) 的子区间合法。据此考虑区间 DP,设 \(f_{l, r}\) 表示往环子区间填入 \(1\) 到 \(\begin{cases} r - l + 1 & l \leq r \\ r + (n - l + 1) & l > r\end{cases}\) 的方案数,转移时 \(\mathcal{O}(1)\) 检查即可。
时间复杂度 \(\mathcal{O}(n^2)\)。
感觉最后一步的构造很需要想象力...只能狂暴膜题解了
code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 5, mod = 1e9 + 7;
typedef long long LL;
void add(int &x, int y) {
x = (x + y >= mod) ? (x + y - mod) : (x + y);
}
int n, m, ans, f[N][N], p[N], q[N], pos[N];
bool in(int l, int r, int x) {
if (l <= r) {
return l <= x && x <= r;
} else {
return l <= x || x <= r;
}
}
bool chk(int l, int r, int x, bool tar) {
bool ok = true;
if (q[x] > 1 && in(l, r, pos[q[x] - 1]) != tar) {
ok = false;
}
if (q[x] < n && in(l, r, pos[q[x] + 1]) != tar) {
ok = false;
}
return ok;
}
void solve() {
cin >> n;
m = (n + 1) / 2, ans = 0;
for (int i = 1; i <= n; i++) cin >> p[i];
int c = 0;
for (int i = 1; i <= n; i += 2) {
q[++c] = i, pos[i] = c;
}
for (int i = n - (n & 1); i >= 1; i -= 2) {
q[++c] = i, pos[i] = c;
}
for (int _ = 0; _ < 2; _++) {
for (int l = 1; l <= n; l++) {
for (int i = 1; i <= n; i++) {
int j = (i + l - 2) % n + 1;
f[i][j] = 0;
if (l == 1) {
if (p[q[i]] == -1 || p[q[i]] == 1)
f[i][i] = (i <= m) ^ _;
continue;
}
if ((p[q[i]] == -1 || p[q[i]] == l) && chk(i, j, i, (i > m) ^ _)) {
add(f[i][j], f[i % n + 1][j]);
if (l == n && ((i > m) ^ _)) add(ans, f[i % n + 1][j]);
}
if ((p[q[j]] == -1 || p[q[j]] == l) && chk(i, j, j, (j > m) ^ _)) {
add(f[i][j], f[i][(j + n - 2) % n + 1]);
}
}
}
}
cout << ans << "\n";
}
int main() {
ios :: sync_with_stdio(false);
cin.tie(nullptr);
int t; cin >> t;
while (t--) solve();
return 0;
}