abc393 题解

ABC

D

题目大意

01串中有若干 1,求至少通过交换多少次相邻的数,能使得所有的 1 是连续的。

解题思路

我用的中位数定理,让全部的 1 往最中间的 1 靠,然后硬算交换了多少次。

但感觉 std 更加巧妙简单,以下是我对 std 的理解:

目标是通过交换让所有的 1 都连续,也就是 1 之间没有 0。于是考虑将 0 从 1 之间移出去,假设当前枚举到的 0 的左边有 x 个 1,右边有 y 个 1,那么不论左右两边的 1 是否连续,将这个 0 向左交换到第一个 1 的位置所需要的交换次数是 x,将这个 0 向右交换到最后一个 1 的位置所需要的交换次数是 y。两种方法都能使 0 交换到 1 外面,所以选交换次数最小的就好了。为什么不论是否连续?因为我们的每一次交换都应该交换 0 和 1。

(粘个用中位数定理写的)

CODE
void solve()
{
    int n = 0;
    std::string s;
    std::cin >> n >> s;
    std::vector<int> pos;
    for (int i = 0; i < n; i++) {
        if (s[i] == '1') {
            pos.push_back(i);
        }
    }

    int mid = (pos.size() - 1) / 2;
    i64 ans = 0;
    for (int i = 0; i <= mid; i++) {
        ans += pos[mid] - pos[i];
    }
    for (int i = mid + 1; i < pos.size(); i++) {
        ans += pos[i] - pos[mid];
    }

    ans -= 1ll * mid * (mid + 1) / 2ll;
    ans -= 1ll * (pos.size() - mid - 1) * (pos.size() - mid) / 2ll;
    std::cout << ans << '\n';
}

E

题目大意

有一个长度为 n 的序列 A,对于每个 i[1,n],求 A 中包含 Aik 个数的 gcd 最大值是多少。

解题思路

假设包含 Aik 个数的 gcd 最大值是 x,则有以下两个必要条件:

  1. x|Ai
  2. A 中至少有 k 个数被 x 整除

则对于每个 Ai 我们可以取枚举它的因数,看满足条件 2. 的最大因数是多少,这个数就是我们要找的 x

但是枚举因数太慢太麻烦了。注意到这题的数据量比一般的大,但是数据范围只有 [1,106]。所以相对于枚举每个数的因数,我们可以从小到大枚举每个满足条件 2. 的倍数。

为了判断是否满足条件 2.,可以维护一个数组 cnt[x],表示序列中有多少数是 x 的倍数。暴力求就好了,复杂度含了一个调和序列。

CODE
void solve()
{
    int n = 0, k = 0;
    std::cin >> n >> k;
    int mx = 0;
    std::vector a(n, 0);
    for (auto &i : a) {
        std::cin >> i;
        cnt[i]++;
        mx = std::max(mx, i);
    }
    for (int i = 1; i <= mx; i++) {
        for (int j = i + i; j <= mx; j += i) {
            cnt[i] += cnt[j];
        }
    }

    for (int i = 1; i <= mx; i++) {
        if (cnt[i] < k) {
            continue;
        }
        for (int j = i; j <= mx; j += i) {
            ans[j] = i;
        }
    }
    for (auto &i : a) {
        std::cout << ans[i] << '\n';
    }
    return;
}

F

题目大意

有序列 A,记序列 B[i]=A1,A2,,Ai。现在有 q 个询问 {Ri,xi} 表示 B[Ri] 中最大值不超过 xi 的 LIS 的长度是多少。

解题思路

对于询问 {Ri,xi} ,在不考虑从 xi 的情况下我们先用常规求 LIS 的方法求出 B[Ri] 的 dp 数组,再在这个 dp 数组里二分找第一个大于 xi 的位置就好了。

显然不需要对每个 B[Ri] 求一个 dp 数组,直接离线就好了。

CODE
void solve()
{
    int n = 0, q = 0;
    std::cin >> n >> q;
    std::vector a(n + 1, 0);
    for (int i = 1; i <= n; i++) {
        std::cin >> a[i];
    }
    std::vector qr(n + 1, std::vector<std::array<int, 2>>{});
    for (int i = 0; i < q; i++) {
        int r = 0, x = 0;
        std::cin >> r >> x;
        qr[r].push_back({ x, i });
    }

    std::vector dp(n + 1, Inf + 5), ans(q, 0);
    dp[0] = 0;
    for (int i = 1; i <= n; i++) {
        dp[std::lower_bound(dp.begin(), dp.end(), a[i]) - dp.begin()] = a[i];
        for (auto &[mx, idx] : qr[i]) {
            ans[idx] = std::upper_bound(dp.begin(), dp.end(), mx) - dp.begin() - 1;
        }
    }

    for (auto &i : ans) {
        std::cout << i << '\n';
    }
    return;
}

G

(看题解的篇幅就知道是做不了一点的题)

posted @   Young_Cloud  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示