首页 > 其他分享 >CF1858D Trees and Segments

CF1858D Trees and Segments

时间:2023-08-26 15:55:51浏览次数:49  
标签:pre suf CF1858D int max Segments len Trees dp

一道考查预处理技巧的 dp。

观察式子 \(a\times L_0+L_1\),一个显然的想法是“定一求一”,即预处理求出对于每个 \(L_1\) 最大的 \(L_0\),然后对于每个 \(a\),枚举 \(L_1\),统计最大的 \(a\times L_0+L_1\)。这样,我们将问题转化为了:已知 \(L_1=len\),求出 \(dp_{len}=L_{0max}\)。

dp 数组的状态是“长度”,这似乎并不好维护,我们不得不把它落实到一个个子段上去:对于每个 \(len\in [1,n]\),枚举长度为 \(len\) 的子段,并用 \(x\) 次操作把它改为一个全 \(1\) 段,这就是 \(L_1\) 对应的子段。然后,在它的左边和右边,去寻找 \(L_0\)(利用好剩下的 \(k-x\) 次操作使其尽可能大)并更新 \(dp_{len}\)。

问题是,仅仅枚举子段的复杂度就到了 \(O(n^2)\),意味着我们需要预处理出前后缀 \(1\sim i\) 中修改至少 \(j\) 次能得到的最大 \(L_0\),分别设为 \(pre_{i,j},suf_{i,j}\)。通用的方法是“两步走”:以前缀为例,先求出以 \(i\) 结尾的,修改了恰好 \(j\) 次的最大 \(L_0\);再应用类似前缀 \(\max\) 的方法得到答案。

启示:

  1. 动态规划设计状态应该尽可能简洁,既能清晰的表示当前情形,又不包含冗余信息;
  2. 研究复杂的问题可以层层深入,把大问题分解为一个个小问题,各个击破。

下面是 AC 代码:

void solve() {
    int n, k;
    string s;
    cin >> n >> k >> s;
    // 以 i 结尾,修改 j 次的最大 0 段长度
    vector<vector<int>> pre(n, vector<int>(k + 1, 0));
    pre[0][0] = (s[0] == '0');
    if (k > 0) {
        pre[0][1] = (s[0] == '1');
    }
    for (int i = 1; i < n; i++) {
        for (int j = 0; j <= min(i + 1, k); j++) {
            if (s[i] == '0') {
                pre[i][j] = pre[i - 1][j] + 1; // s[i] = 0, 不修改
            } else if (j > 0) {
                pre[i][j] = pre[i - 1][j - 1] + 1; // s[i] = 1, 修改
            } else {
                pre[i][j] = 0; // s[i] = 1, 不修改
            }
        }
    }
    // 将 pre 改为在 i 及 i 之前结尾,最多修改 j 次的最大 0 段长度
    for (int i = 1; i < n; i++) {
        for (int j = 0; j <= k; j++) {
            pre[i][j] = max(pre[i][j], pre[i - 1][j]);
            if (j > 0) {
                pre[i][j] = max(pre[i][j], pre[i][j - 1]);
            }
        }
    }
    // 以 i 开头,修改 j 次的最大 0 段长度
    vector<vector<int>> suf(n, vector<int>(k + 1, 0));
    suf[n - 1][0] = (s[n - 1] == '0');
    if (k > 0) {
        suf[n - 1][1] = (s[n - 1] == '1');
    }
    for (int i = n - 2; i >= 0; i--) {
        for (int j = 0; j <= min(n - i, k); j++) {
            if (s[i] == '0') {
                suf[i][j] = suf[i + 1][j] + 1; // s[i] = 0, 不修改
            } else if (j > 0) {
                suf[i][j] = suf[i + 1][j - 1] + 1; // s[i] = 1, 修改
            } else {
                suf[i][j] = 0; // s[i] = 1, 不修改
            }
        }
    }
    // 将 suf 改为在 i 及 i 之后开头,最多修改 j 次的最大 0 段长度
    for (int i = n - 2; i >= 0; i--) {
        for (int j = 0; j <= k; j++) {
            suf[i][j] = max(suf[i][j], suf[i + 1][j]);
            if (j > 0) {
                suf[i][j] = max(suf[i][j], suf[i][j - 1]);
            }
        }
    }
    vector<int> sum(n, 0);
    sum[0] = s[0] - '0';
    for (int i = 1; i < n; i++) {
        sum[i] = sum[i - 1] + s[i] - '0';
    }
    // dp[len] 为 L1 = len 时的最大 L0
    vector<int> dp(n + 1, -1);
    dp[0] = max(pre[n - 1][k], suf[0][k]);
    for (int l = 0; l < n; l++) {
        for (int r = l; r < n; r++) {
            int len = r - l + 1;
            // [l, r] 中 0 的个数
            int x = len - (sum[r] - (l > 0 ? sum[l - 1] : 0));
            if (x > k) {
                continue;
            }
            if (l == 0 && r == n - 1) {
                dp[len] = max(dp[len], 0);
                continue;
            }
            if (l > 0) {
                dp[len] = max(dp[len], pre[l - 1][k - x]);
            }
            if (r < n - 1) {
                dp[len] = max(dp[len], suf[r + 1][k - x]);
            }
        }
    }
    for (int a = 1; a <= n; a++) {
        int ans = 0;
        for (int len = 0; len <= n; len++) {
            if (dp[len] == -1) {
                continue;
            }
            ans = max(ans, a * dp[len] + len);
        }
        cout << ans << " ";
    }
    cout << endl;
}

THE END

标签:pre,suf,CF1858D,int,max,Segments,len,Trees,dp
From: https://www.cnblogs.com/th19/p/17657923.html

相关文章

  • vue-treeselect 树下拉组件被遮挡问题
    vue-treeselect组件官方中文网站: https://www.vue-treeselect.cn/需求背景:在el-tabs内容中添加此组件出现被遮挡问题通过文档查询解决方法<treeselectv-model="params.wardIds":options="hospitalWardTree"value-consists-of="LEAF_PRIORITY"placeholder=......
  • D. Trees and Segments
    D.TreesandSegmentsTheteachersoftheSummerInformaticsSchooldecidedtoplant$n$treesinarow,anditwasdecidedtoplantonlyoaksandfirs.Todothis,theymadeaplan,whichcanberepresentedasabinarystring$s$oflength$n$.If$s_i=......
  • vue-treeselect 校验失败添加红框
    需求vue-treeselect校验及清除校验这篇介绍了用@input在校验失败时显示校验信息。但还需要同时显示红色边框。如下图所示:解决办法思路:动态绑定类名,校验失败时切换类名,更改边框颜色。子组件SelectTree二次封装vue-treeselect:组件SelectTree<template><divclass=......
  • LeetCode 7022——熟悉TreeSet数据结构及常用方法的使用
    LeetCode7022.限制条件下元素之间的最小绝对差题目描述:给你一个下标从 0 开始的整数数组 nums 和一个整数 x 。请你找到数组中下标距离至少为 x 的两个元素的 差值绝对值 的 最小值 。换言之,请你找到两个下标 i 和 j ,满足 abs(i-j)>=x 且 abs(nums[i......
  • Paper Reading: NBDT: Neural-Backed Decision Trees
    目录研究动机文章贡献本文方法推理建立层次结构用WordNet标记决策节点微调和树监督损失实验结果对比实验结果可解释性识别错误的模型预测引导图像分类人更倾向的解释识别有缺陷的数据标签优点和创新点PaperReading是从个人角度进行的一些总结分享,受到个人关注点的侧重和实力......
  • 全局引用ant-design-vue的TreeSelect没有样式
    在全局只按需引用了TreeSelect---vue.use(TreeSelect)没有样式.........需要在babel.config.js里的plugins配置plugins:[["import",{"libraryName":"ant-design-vue",//库名称"libraryDirectory"......
  • [LeetCode] 894. All Possible Full Binary Trees
    Givenaninteger n,return alistofallpossible fullbinarytrees with n nodes.Eachnodeofeachtreeintheanswermusthave Node.val==0.Eachelementoftheansweristherootnodeofonepossibletree.Youmayreturnthefinallistoftreesin......
  • CF633G Yash And Trees
    简单题。先把树拍扁成序列,在dfn序上区间修改区间查询。由于时限4s,我们可以整点怪的,比如bitset。把区间内的数有/没有表示成\(01\)序列,考虑到区间加取模相当于区间内的数全部循环右移,用bitset可以做到\(O(\fracm\omega)\)。然后用线段树维护这个序列就行了,查询的时......
  • CF997E Good Subsegments
    简单题,不知道为啥和弱化版一个Difficulty。考虑弱化怎么做。区间\([l,r]\)中的数是连续的,当且仅当区间最大值\(\max\)减去区间最小值\(\min\)为\(r-l\),即\(\max-\min=r-l\)。考虑扫描线,固定右端点\(r\),统计每个左端点的贡献。由于\(S(l,r)=\text{max}-\text{min}+l......
  • 1843E - Tracking Segments
    Problem-E-Codeforces题意是现在有n个0,给你m段序列,然后给你q次操作,每次操作给一个x,把第x个0变成1,问你最少几次操作能出现一段序列里的1的数量大于0的数量,如果不存在,输出-1对于操作数是一个递增序列。如果第k次操作后正好可行,那么就不用管k+1及以后了。所以可以使用二分来解......