CF1858D Trees and Segments

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

观察式子 a×L0+L1,一个显然的想法是“定一求一”,即预处理求出对于每个 L1 最大的 L0,然后对于每个 a,枚举 L1,统计最大的 a×L0+L1。这样,我们将问题转化为了:已知 L1=len,求出 dplen=L0max

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

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

void solve() {
    int n, k; string s;
    cin >> n >> k >> s;
    // 以 i 结尾,修改 j 次的最大 0 段长度
    vector pre(n, vector(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 suf(n, vector<(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, ans = 0; a <= n; a++) {
        for (int len = 0; len <= n; len++)
            if (dp[len] != -1) ans = max(ans, a * dp[len] + len);
        cout << ans << " \n"[a == n];
    }
}
posted @   XYukari  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示