专栏文章

CF 2003F Turtle and Three Sequences

CF2003F题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mipkujnv
此快照首次捕获于
2025/12/03 13:39
3 个月前
此快照最后确认于
2025/12/03 13:39
3 个月前
查看原文
首先分析一下这题的限制 pi<pi+1,api<api+1p_i < p_{i + 1}, a_{p_i} < a_{p_{i + 1}}bpibpjb_{p_i} \not= b_{p_j}
相比之下,前两个限制更简单,因为其只限制于相邻的两数之间,而后一个限制要对所有 i,ji, j 满足,只知道 bpi1b_{p_{i - 1}} 是不够的。
对于这种 \not = 的限制,一个想法是暴力钦定一些相同的情况(贝尔数)容斥,但因为这个题是最值相关,容斥在此也没有办法。
考虑这个限制为什么会很难处理,因为必须要知道前缀选的 bb 的信息,但是这个状态数是 (nm)+(nm1)++(n0)\binom{n}{m} + \binom{n}{m - 1} + \cdots + \binom{n}{0} 的,数量太过庞大所以不能直接用。
所以发现其实问题都出在这个 (nm)\binom{n}{m} 中的 nn,也就是 bb 的种类数,本质上就是因为种类过多导致所需信息太多。
那一个想法就是考虑减少种类数使得所需的信息在可控的范围内
于是可以考虑把 bb 的值域限定在 [0,B)[0, B)BB 个数中。
因为 bi=bjb_i = b_j 肯定是不能被选在一起的,所以如果 bb 的值相同被限定后的值也必须相同,所以这个限定就可以当作是从 bb 的值域到 [0,B)[0, B) 的映射。
但是又有问题,如何映射,又如何保证最优解会出现?
此时的一个做法是随机映射,考虑最优解的 mm 个值,那么其 bb 的映射的方案数为 BmB^m;若该最优解可以表示出,那么这 mm 个值对应的 bb 的值就应该不同,对应方案数为 (Bm)×m!\binom{B}{m}\times m!
于是可以得到一个单次随机出最优解的概率:(Bm)×m!Bm\frac{\binom{B}{m}\times m!}{B^m}。单次正确的概率可能不大,于是考虑多随几次,随机个 TT 次,正确率就变为 1(1(Bm)×m!Bm)T1 - \bigg(1 - \frac{\binom{B}{m}\times m!}{B^m}\bigg)^T(其实因为要求每个测试点都正确,所以通过的概率应该还需要有一个测试点的次方)。
接下来考虑把 bb 的值域映射到了 [0,B)[0, B) 后如何处理答案。
此时再考虑 pi<pi+1,api<api+1p_i < p_{i + 1}, a_{p_i} < a_{p_{i + 1}} 的限制,考虑 DP,设计 fi,sf_{i, s} 表示考虑了 [1,i][1, i] 并且选的最后一个为 iibb 中选的集合为 ss 的最大权值。
转移就直接考虑这些限制即可:fi,sfj,s{bi}+ci(j<i,ajai,bis)f_{i, s}\leftarrow f_{j, s - \{b_i\}} + c_i(j < i, a_j\le a_i, b_i\in s)
平衡这个查询和加入的复杂度,用一个树状数组优化即可。
最后时间复杂度 O(T2Bnlogn)\mathcal{O}(T2^Bn\log n),正确率 1(1(Bm)×m!Bm)T1 - \bigg(1 - \frac{\binom{B}{m}\times m!}{B^m}\bigg)^T,可以取 B=7,T=150B = 7, T = 150
CPP
#include<bits/stdc++.h>
constexpr int maxn = 3e3 + 2, B = 7;
using arr = std::array<int, 1 << B>;
inline arr max(const arr &a, const arr &b) {
    arr c;
    for (int i = 0; i < (1 << B); i++) c[i] = std::max(a[i], b[i]);
    return c; 
}
int n, m;
arr tr[maxn];
inline void clear() {
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j < (1 << B); j++) {
            tr[i][j] = j == 0 ? 0 : (int)-1e9;
        }
    }
}
inline void add(int x, const arr &y) {
    for (; x <= n; x += x & -x) tr[x] = max(tr[x], y);
}
inline arr query(int x) {
    arr y = tr[x];
    for (; x >= 1; x -= x & -x) y = max(y, tr[x]);
    return y;
}
std::mt19937_64 rnd(std::chrono::steady_clock::now().time_since_epoch().count());
int a[maxn], b_[maxn], c[maxn];
int pb[maxn], b[maxn];
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for (int i = 1; i <= n; i++) scanf("%d", &b_[i]);
    for (int i = 1; i <= n; i++) scanf("%d", &c[i]);
    int ans = -1;
    for (int t = 0; t < 150; t++) {
        for (int i = 1; i <= n; i++) pb[i] = rnd() % B;
        for (int i = 1; i <= n; i++) b[i] = pb[b_[i]];
        clear();
        for (int i = 1; i <= n; i++) {
            arr f = query(a[i]);
            for (int s = 0; s < (1 << B); s++) {
                if (~ s >> b[i] & 1) {
                    f[s | (1 << b[i])] = std::max(f[s | (1 << b[i])], f[s] + c[i]);
                }
            }
            add(a[i], f);
        }
        const arr f = query(n);
        for (int s = 0; s < (1 << B); s++) {
            if (__builtin_popcount(s) == m) {
                ans = std::max(ans, f[s]);
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...