CF2185H BattleCows 2 题解

这是此博客的第一篇文章,从洛谷上搬了过来,也起到测试作用。

此题有诸多做法,这里提供一种码量、复杂度较小而思维量较大的解法。

注意到当奶牛处理完左侧区间后,右侧的合并操作对哪个位置都是如出一辙的,当且仅当在 \(a_x > sum_{x-1}\) 的位置需要作弊(\(sum\) 是前缀和)。这样的位置至多只有 \(\text{log}A\) 个,因为每次触发条件都会使前缀和翻倍。

因此我们可以先预处理存下这样的位置,并方便地算出每头牛在位置 \(1\) 时的作弊数,记为 \(h\),同时处理出此时需要作弊的位置集合 \(S\)此时可以断言,这头牛在任意位置的最大作弊数为 \(h+1\)。(与位置 \(1\) 相比唯一能多产生作弊数的地方在于和左侧的合并,向右合并的作弊数一定 \(\leq h\))。

可以分析得到牛根据位置的作弊数分布一开始保持为 \(h\) ,而后保持为 \(h+1\)(长度可以是 \(0\)),最后再递减回 \(0\)\(1\)

显然若 \(h < k\),该牛的答案直接为 \(n\)

\(h > k\),只会在递减的后缀中有贡献。设正在处理原始位置为 \(i\) 的奶牛,可以直接找到 \(S\) 的倒数第 \(k\) 个位置 \(p\),并有一个简易的分类讨论。

  • \(p > i\),将 \(i\) 移动致 \(p\)\(p\)\(i\) 的左边,此时 \(p\) 就是最大的满足条件的后缀。(当移动到比 \(p\) 更小的位置时左侧依然有合并贡献,总贡献会 \(>k\))。
  • \(p < i\),将 \(i\) 移动致 \(p\)\(p\)\(i\) 的右边,因为左侧有合并贡献导致此时总贡献为 \(k+1\),所以 \(p+1\) 才是最小的后缀。

最后剩的 \(h=k\) 情况,先加上上述讨论算出的后缀答案,注意这里算出的后缀答案前提是左侧需要合并。而后可以直接分情况二分出最大的满足左侧无需合并的临界点,这个临界点对应的前缀一定都是符合条件的(因为一定 \(\leq h\))。可知这段前缀和后缀一定不交,并且可以不重不漏统计完所有答案。

时间复杂度 \(O(n (\text{log}n + \text{log}A))\)

void solve() {
    int n, k;
    std::cin >> n >> k;
    
    std::vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) {
        std::cin >> a[i];
    }
    std::vector<i64> sum(n + 1);
    for (int i = 1; i <= n; i++) {
        sum[i] = sum[i - 1] + a[i];
    }

    std::vector<int> pos;
    for (int i = 1; i <= n; i++) {
        if (a[i] > sum[i - 1]) {
            pos.push_back(i);
        }
    }
    std::vector<int> ans(n + 1);
    for (int i = 1; i <= n; i++) {
        std::vector<int> g;
        for (int x : pos) {
            if (x != i) {
                if (a[x] > sum[x - 1] + (x < i) * a[i]) {
                    g.push_back(x);
                }
            }
        }
        if (g.size() < k) {
            ans[i] = n;
            continue;
        }
        if (k > 0) {
            int p = g.end()[-k];
            ans[i] += n - p + 1 - (p < i);
        }
        if (g.size() == k) {
            if (sum[i - 1] >= a[i]) {
                ans[i] += std::lower_bound(sum.begin() + 1, sum.end(), a[i]) - sum.begin();
            } else {
                ans[i] += std::lower_bound(sum.begin() + 1, sum.end(), 2 * a[i]) - sum.begin() - 1;
            }
        }
    }

    for (int i = 1; i <= n; i++) {
        std::cout << ans[i] <<  " \n"[i == n];
    }
}
posted @ 2026-01-24 05:53  c0Sai  阅读(5)  评论(0)    收藏  举报