CF526G Spiders Evil Plan 题解

作者: xht37 分类: 题解 发布时间: 2020-01-08 22:07

Visits: 267

CF526G Spiders Evil Plan

题意

  • 给定一棵 $n$ 个节点的无根树,每条边有边权。
  • 有 $q$ 次询问,每次询问给出 $x,y$,你需要选择 $y$ 条树上的路径,使这些路径形成一个包含 $x$ 的连通块,且连通块中包含的边权和最大。
  • $n, q \le 10^5$,强制在线。

题解

设叶子个数为 $k$。

首先,若 $2y \ge k$,则 $y$ 条路径可以直接覆盖整棵树。

否则,每条路径的两端一定是两个叶子,即一共要选择 $2y$ 个叶子。

考虑以 $x$ 为根时,我们如何选择叶子呢?

注意到,如果一个节点 $x$ 的子树中有叶子被选了,那么 $x$ 所在长链包含的叶子一定也被选择了。

于是对于每一个叶子,它的贡献实际上等于它到它所在的长链的顶点的父亲的距离。

那么我们就可以按贡献从大到小选择叶子了。

但如果每次询问都以 $x$ 为根进行一次 $\mathcal O(n)$ 的长链剖分,复杂度显然太高。

但又可以注意到,在树上经过点 $x$ 的最长链一定经过直径的某一端。

于是我们只需要以直径两端的两个点为根分别做一次就好了。由于直径两端一定是叶子,因此这个时候我们只应该选择 $2y-1$ 个叶子。

但这么做有个问题,如果选择的叶子组成的连通块并不包括 $x$ 怎么办?

可以发现只有两种情况:

  1. 将贡献最小的长链去掉并加入 $x$ 所在长链。
  2. 是找到离 $x$ 最近的长链将下半部分替换成 $x$ 所在长链。

对于两种情况,都需要向上找到第一个不在贡献内的点。如果暴力跳长链,在边权都为 $1$ 的情况下是可行的,因为这样暴力跳的复杂度为 $\mathcal O(\sqrt n)$。

但这道题边带权,因此暴力跳的复杂度为 $\mathcal O(n)$,所以还需要倍增往上找,复杂度为 $\mathcal O(\log n)$。

总时间复杂度 $\mathcal O((n + q) \log n)$。

代码

const int N = 1e5 + 7;
int n, q, k, s, ans;
vector <pi> e[N];
struct T {
    int rt, f[N][21], g[N][21], d[N], dep[N], son[N], top[N], rnk[N];
    int l[N], r[N], s[N], t;

    void dfs1(int x, int fa) {
        for (pi o : e[x])
            if (o.fi != fa) d[o.fi] = d[x] + o.se, dfs1(o.fi, x);
    }

    void dfs2(int x) {
        for (pi o : e[x])
            if (o.fi != f[x][0]) {
                f[o.fi][0] = x, g[o.fi][0] = o.se;
                for (int i = 0; f[o.fi][i]; i++)
                    f[o.fi][i+1] = f[f[o.fi][i]][i],
                    g[o.fi][i+1] = g[o.fi][i] + g[f[o.fi][i]][i];
                d[o.fi] = d[x] + o.se, dfs2(o.fi);
                if (dep[o.fi] + o.se > dep[x])
                    dep[x] = dep[o.fi] + o.se, son[x] = o.fi;;
            }
        for (pi o : e[x])
            if (o.fi != f[x][0] && o.fi != son[x])
                s[l[++t]=o.fi] = dep[o.fi] + o.se;
    }

    void getrt(int x) {
        dfs1(x, 0), rt = x;
        for (int i = 1; i <= n; i++) if (d[i] > d[rt]) rt = i;
        d[rt] = 0, dfs2(rt), s[l[++t]=rt] = dep[rt];
        sort(l + 1, l + t + 1, [&](int i, int j) { return s[i] > s[j]; });
        for (int i = 1; i <= t; i++) r[i] = r[i-1] + s[l[i]];
        for (int i = 1; i <= t; i++) {
            int x = l[i], p = x;
            while (x) top[x] = p, rnk[x] = i, x = son[x];
        }
    }

    inline int plan1(int x, int y) {
        int z = dep[x];
        for (int i = 20; ~i; i--)
            if (rnk[f[x][i]] >= y) z += g[x][i], x = f[x][i];
        return r[y-1] + z + g[x][0];
    }

    inline int plan2(int x, int y) {
        int z = dep[x];
        for (int i = 20; ~i; i--)
            if (rnk[f[x][i]] > y) z += g[x][i], x = f[x][i];
        return r[y] - dep[f[x][0]] + z + g[x][0];
    }

    inline int ask(int x, int y) {
        y = 2 * y - 1;
        return rnk[x] <= y ? r[y] : max(plan1(x, y), plan2(x, y));
    }
} t[2];

int main() {
    rd(n), rd(q);
    for (int i = 1, x, y, z; i < n; i++)
        rd(x), rd(y), rd(z),
        e[x].pb(mp(y, z)), e[y].pb(mp(x, z)), s += z;
    for (int i = 1; i <= n; i++) k += e[i].size() == 1u;
    t[0].getrt(1), t[1].getrt(t[0].rt);
    for (int i = 1, x, y; i <= q; i++)
        rd(x), rd(y),
        x = (x + ans - 1) % n + 1, y = (y + ans - 1) % n + 1,
        print(ans = 2 * y >= k ? s : max(t[0].ask(x, y), t[1].ask(x, y)));
    return 0;
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注