关于 wqs 二分的答案输出

P4383 [八省联考 2018] 林克卡特树

Nuisdete @ 2023-02-10 16:46:21

RT.

目前只从惩罚角度感性理解 wqs 二分,对于最后答案的输出有个问题,我记得最后输出的答案应该是二分到的答案 \times 题目限制的 k + 1 个联通块,但是这样会 85 分,代码如下:

#include <cstdio>
#include <vector>
#include <utility>

#define fst first
#define sec second

typedef long long LL;
typedef std::pair<int, int> pii;

const int MAXN = 3e5 + 5;
const LL INF = 0x3f3f3f3f3f3f3f3f;

int n, m;

struct Str {
    LL val; int cnt;
    Str operator+ (const Str& A) const {
        Str res;
        res.val = val + A.val, res.cnt = cnt + A.cnt;
        return res;
    }
} f[MAXN << 1][3];

std::vector<pii> G[MAXN << 1];

LL ext;

Str MAX(Str A, const Str B) {
    if (A.val == B.val) return A.cnt > B.cnt ? A : B;
    return A.val > B.val ? A : B;
}

void dfs(int u, int father) {
    for (auto x : G[u]) {
        int k = x.fst, val = x.sec;
        if (k != father) {
            dfs(k, u);
            f[u][2] = MAX(f[u][2] + f[k][0], f[u][1] + f[k][1] + (Str){ val - ext, -1 });
            f[u][1] = MAX(f[u][1] + f[k][0], f[u][0] + f[k][1] + (Str){ val, 0 });
            f[u][0] = f[u][0] + f[k][0];
        }
    }
    f[u][0] = MAX(MAX(f[u][0], f[u][1]), f[u][2]);
}

void solve(LL mid) {
    ext = mid;
    for (int i = 1; i <= n; ++i) {
        f[i][0] = (Str){ 0LL, 0 };
        f[i][1] = (Str){ ext, 1 };
        f[i][2] = (Str){ -INF, 0 };
    }
    dfs(1, 0);
}

int main() {

    scanf("%d %d", &n, &m); ++m;
    for (int i = 1; i < n; ++i) {
        int x, y, val;
        scanf("%d %d %d", &x, &y, &val);
        G[x].emplace_back(y, val), G[y].emplace_back(x, val);
    }
    LL l = -1e12, r = 1e12, res;
    while (l <= r) {
        LL mid = l + r >> 1;
        solve(mid);
        if (f[1][0].cnt <= m) res = mid, l = mid + 1;
        else r = mid - 1;
    }
    solve(res);
    printf("%lld\n", f[1][0].val - res * m);

    return 0;
}

发现之前的讨论帖也有人有这个问题:link

按照下面人的回答更改了代码并通过了此题,但是似乎与 wqs 二分的正确的答案输出相反,而且题解都是按照我理解的正确的输出方式输出的。

是代码有 bug 并碰巧数据水然后过了吗?


by Rosaya @ 2023-02-10 17:26:41

如果斜率相同的话可能是正好切不到横坐标为 k 的点的,但是这时候因为斜率相同正好可以拿端点值算,所以应该输出端点值。


by Nuisdete @ 2023-02-10 17:37:32

@Dark_night_qwq 但是题目要求恰好为 k,端点值并不一定与横坐标为 k 的值相等吧。我的理解是此时端点的斜率与横坐标为 k 的相等,所以可以计算出横坐标为 k 时的答案。


by Nuisdete @ 2023-02-10 17:38:35

但是为什么要输出端点值?


by Rosaya @ 2023-02-10 18:27:35

@louis_11 哦,我知道了,是您前面的 max 有问题。

就是您让值相同时选 cnt 大的,导致如果斜率相同的话切的是大的。

这时候因为 cnt>m 导致答案没有更新,所以切错了。

我前面纯纯扯淡了。


by Nuisdete @ 2023-02-10 18:48:08

@Dark_night_qwq orz 谢谢您。


|