Loading

P14534 [RMI 2018] W

考虑对于每种值从小到大去放,设 \(f_{i, s}\) 表示到了第 \(i\) 种数,\(5\) 个峰谷的选择情况(有没有被确定)的方案数,转移时枚举超集转移即可,需要注意一些特殊的边界情况以及 \(s\) 的合法性(不然可能会有重)。

如果朴素这么做绝对会重,你可以选择强制必须先选峰再选谷(这样比较好转移且本身去重),这样就比较好写了。

这种排列计数就是通过数分类插入排列的同时维护排列形态。

从别的地方找的代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3e5 + 5, M = 1e6 + 5, P = 1000000007, inv2 = (P + 1) / 2;
int n, a[N], fac[N], inv[N], f[N][1 << 5], cnt[M];
set<int>s;
void init() {
    fac[0] = fac[1] = inv[0] = inv[1] = 1;

    for (int i = 2; i < N; i++)
        fac[i] = fac[i - 1] * i % P, inv[i] = (P - P / i) * inv[P % i] % P;

    for (int i = 2; i < N; i++)
        inv[i] = inv[i - 1] * inv[i] % P;
}
int C(int n, int m) {
    if (!m)
        return 1;

    if (n < m)
        return 0;

    return fac[n] * inv[n - m] % P * inv[m] % P;
}
bool check(int s) {
    if ((s >> 1 & 1) && (!(s & 1) || !(s >> 2 & 1)))
        return 0;

    if ((s >> 3 & 1) && (!(s >> 2 & 1) || !(s >> 4 & 1)))
        return 0;

    return 1;
}
signed main() {
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> n;
    init();

    for (int i = 1; i <= n; i++)
        cin >> a[i], cnt[a[i]]++;

    sort(a + 1, a + 1 + n);
    int t = unique(a + 1, a + 1 + n) - a - 1;
    vector<int>vt;
    vt.push_back(0);

    for (int i = t; i >= 1; i--)
        vt.push_back(cnt[a[i]]);

    f[0][0] = 1;

    for (int i = 1; i <= t; i++) {
        for (int s = 0; s < 1 << 5; s++) {
            if (!check(s))
                continue;

            for (int t = s; t < 1 << 5; t = ((t + 1) | s)) {
                if (!check(t))
                    continue;

                if ((t >> 1 & 1) && (!(s & 1) || !(s >> 2 & 1)))
                    continue;

                if ((t >> 3 & 1) && (!(s >> 2 & 1) || !(s >> 4 & 1)))
                    continue;

                int ct = __builtin_popcount(s ^ t), rest = vt[i] - ct;

                if (!(t >> 1 & 1) && (s >> 2 & 1))
                    ct++;

                if (!(t >> 1 & 1) && (s & 1))
                    ct++;

                if (!(t >> 3 & 1) && (s >> 2 & 1))
                    ct++;

                if (!(t >> 3 & 1) && (s >> 4 & 1))
                    ct++;

                f[i][t] = (f[i][t] + f[i - 1][s] * C(ct + rest - 1, ct - 1) % P) % P;
            }
        }
    }

    cout << f[vt.size() - 1][(1 << 5) - 1];

    return 0;
}
posted @ 2025-11-21 17:27  Alexande  阅读(4)  评论(0)    收藏  举报