题解:AT_arc138_f [ARC138F] KD Tree

· · 题解

我们考虑记状态为 f_{l,r,u,d} 表示计算划分点集 S=\{i|l\le i\le r,u\le p_i\le d\} 的方案数。

注意到由于同一个点集排列可能有多种划分方式,则我们的思路是考虑计算字典序最小的划分方式。

此时,如何钦定操作的字典序就非常关键,这道题里的排列方式为:

我们记状态:

我们考虑转移:

时间复杂度 O(n^6)

// MagicDark
#include <bits/stdc++.h>
#define debug cerr << "\33[32m[" << __LINE__ << "]\33[m "
#define SZ(x) ((int) x.size() - 1)
#define all(x) x.begin(), x.end()
#define ms(x, y) memset(x, y, sizeof x)
#define F(i, x, y) for (int i = (x); i <= (y); i++)
#define DF(i, x, y) for (int i = (x); i >= (y); i--)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
template <typename T> T& chkmax(T& x, T y) {return x = max(x, y);}
template <typename T> T& chkmin(T& x, T y) {return x = min(x, y);}
template <typename T> T& read(T &x) {
    x = 0; int f = 1; char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') f = - f;
    for (; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x *= f;
}
const int N = 35, MOD = 1e9 + 7;
inline int& add(int &x, ll y) {return x = (x + y) % MOD;}
int n, p[N], q[N];
unordered_map <int, int> mp;
int dfs(int x) {
    if (__builtin_popcount(x) <= 1) return 1;
    if (mp.count(x)) return mp[x];
    vector <int> a, b;
    for (int i = 0; i < n; i++)
        if ((x >> i) & 1) a.push_back(i), b.push_back(p[i]);
    sort(all(b));
    vector <int> g;
    int s1 = 0, s2 = 0;
    F(i, 0, SZ(a) - 1) {
        g.push_back(s1 |= 1 << a[i]);
        g.push_back(s2 |= 1 << q[b[i]]);
    }
    vector <int> f(g.size());
    int ans = 0;
    F(i, 0, SZ(g)) {
        f[i] = dfs(g[i]);
        F(j, 0, i - 1)
            if ((g[i] & g[j]) == g[j]) add(f[i], MOD - (ll) f[j] * dfs(g[i] ^ g[j]) % MOD);
        add(ans, (ll) f[i] * dfs(x ^ g[i]));
    }
    return mp[x] = ans;
}
signed main() {
    read(n);
    for (int i = 0; i < n; i++) q[--read(p[i])] = i;
    cout << dfs((1 << n) - 1);
    return 0;
}