首页 > 其他分享 >CF1761F Anti-median (Easy Version)

CF1761F Anti-median (Easy Version)

时间:2023-01-10 23:58:05浏览次数:51  
标签:排列 奇数 int median pos leq Version Anti &&

称一个排列是好的,当且仅当对于所有 \(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;   
}

标签:排列,奇数,int,median,pos,leq,Version,Anti,&&
From: https://www.cnblogs.com/came11ia/p/17041557.html

相关文章