首页 > 其他分享 >题解 accoders::NOI 5511【漂亮轰炸(bomb)】

题解 accoders::NOI 5511【漂亮轰炸(bomb)】

时间:2023-10-04 21:22:41浏览次数:40  
标签:bomb NOI int 题解 sum bty ycs include hei

题解 accoders::NOI 5511【漂亮轰炸(bomb)】

http://47.92.197.167:5283/contest/406/problem/4

BZOJ3252 是弱化版。

problem

一棵树,边带权。\(Q\) 次询问,给定 \(k\) 和一个首都点,选择 \(k\) 条路径轰炸,其中必须由一轮要轰炸首都,但没有要求每条路径都经过首都。每条边只能被炸一次,贡献值算一次,求被炸到的边的总和的最大值。\(n,Q\leq 500000\)。

solution

  1. 选出的路径必须有一条的一端是直径端点。否则我可以换。
  2. 选出的路径两端必须是叶子,将路径扩展的叶子不定不会更劣。

考虑随便跳出一条直径,以直径两端分别为根跑两次。以下考虑其中一个。

我们怎么求答案呢?

  • 选出一个叶子和根连起来,选出其他 \(2(k-1)\) 个叶子,按照某种 dfn 序交错的方法可以选出这 \(2k-1\) 个叶子和根组成的虚树上的所有边权。
  • 通过其他方式使得这个虚树经过首都。

考虑对树做长链剖分。意思是定义了 \(hei_i\) 表示点 \(i\) 往下到最深的叶子的距离(带权),然后区分出长儿子和轻儿子,有了长链剖分。

这样的长链剖分有性质:

  • 对于点 \(u\),点 \(u\) 所在长链权值(记为 \(sum_u\),算上链顶的轻边)$\geq $ 它的任意一个轻儿子的所在长链权值。否则他不叫轻儿子。
  • 考虑如果我们将所有长链排序后选出前 \(2k-1\) 大的链,则这些 链 接一起 一定连通了,然后可以通过神秘 dfn 序成功构造方案。

所以我们现在有了一个方案。需要调整之。

如果选到了首都,那么这个方案最优了。

否则考虑换掉一条链。考虑如果换掉 \(y\):

  • 如果 \(y\) 的链顶不是首都 \(x\) 的祖先,它们互不影响,去掉 \(y\) 整条链,接上 \(x\)。我们可以选最短的链换掉,断言不可能有依赖。

  • 如果 \(y\) 的链顶是首都 \(x\) 的祖先,则去掉 \(lca(x, y)\to y\) 这一段,加上 \(x\to lca(x, y)\)。

  • 注意我们默认 \(lca(x, y)\) 子树内只有 \(y\) 一条链,因为如果有多条链,会以第一种 \(y\) 被枚举到,或者第一种 \(y\) 更优。

考虑 \(lca(x, y)\) 我们取 \(x\) 向上跳祖先跳到的第一个有链被选的地方,记为 \(p\),然后换掉 \(p\) 下面的一条链即可。

\(O((n+Q)\log n)\)。

code

#include <cstdio>
#include <vector>
#include <tuple>
#include <utility>
#include <cstring>
#include <cassert>
#include <algorithm>
#include <functional>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr, ##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
int n, Q;
vector<pair<int, int>> g[1 << 19];
struct Tree {
    int root, son[1 << 19], bty[1 << 19], rnk[1 << 19], cnt;
    LL hei[1 << 19], dep[1 << 19], sum[1 << 19];
    int fa[20][1 << 19], *ft = fa[0], H[1 << 19];
    vector<pair<LL, int>> ycs;
    void dfs(int u, int F) {
        ft[u] = F;
        bty[u] = u;
        rnk[++cnt] = u;
        for (auto &&edge: g[u]) {
            int v, w; tie(v, w) = edge;
            if (v == F) continue;
            dep[v] = dep[u] + w;
            dfs(v, u);
            if (hei[u] < hei[v] + w) {
                hei[u] = hei[v] + w;
                son[u] = v;
                bty[u] = bty[v];
            }
        }
        for (auto &&edge: g[u]) {
            int v, w; tie(v, w) = edge;
            if (v == F) continue;
            if (son[u] != v) ycs.emplace_back(hei[v] + w, bty[v]);
        }
    }
    void build(int rt) {
        debug("choose root = %d\n", rt);
        dfs(root = rt, 0);
        ycs.emplace_back(hei[rt], bty[rt]);
        //sort(ycs.begin(), ycs.end(), greater<pair<LL, int>>());
        sort(ycs.begin(), ycs.end(), greater<pair<int, int>>());
        for (auto e: ycs) debug("{%lld [%d]}, ", e.first, e.second);
        debug("\n");
        sum[0] = ycs[0].first;
        for (int i = 1; i < ycs.size(); i++) sum[i] = sum[i - 1] + ycs[i].first;
        memset(H, 0x3f, sizeof H);
        for (int i = 0; i < ycs.size(); i++) H[ycs[i].second] = i;
        for (int i = n; i >= 1; i--) {
            int u = rnk[i];
            H[ft[u]] = min(H[ft[u]], H[u]);
        }
        for (int j = 1; j <= 19; j++) {
            for (int u = 1; u <= n; u++)
                fa[j][u] = fa[j - 1][fa[j - 1][u]];
        }
    }
    LL query(int u, int k) {
        if (k >= ycs.size()) return sum[ycs.size() - 1];
        if (H[u] < k) return sum[k - 1];
        LL now = sum[k - 1], c = dep[bty[u]];
        int p = u;
        for (int j = 19; j >= 0; j--) if (H[fa[j][p]] >= k) p = fa[j][p];
        p = ft[p], assert(p && H[p] < k);
        c -= dep[p];
        return  max(now - hei[p] + c, now - ycs[k - 1].first + c);
    }
};
Tree T1, T2;
int bfs(int s) {
    queue<int> q;
    static LL dis[1 << 19];
    memset(dis, 0x3f, sizeof dis);
    int last = s;
    for (q.push(s), dis[s] = 0; !q.empty(); ) {
        int u = q.front(); q.pop();
        if (dis[u] > dis[last]) last = u;
        for (auto &&edge: g[u]) {
            int v, w; tie(v, w) = edge;
            if (dis[v] > dis[u] + w) {
                dis[v] = dis[u] + w;
                q.push(v);
            }
        }
    }
    return last;
}
int main() {
#ifndef Robin
    freopen("bomb.in", "r", stdin);
    freopen("bomb.out", "w", stdout);
#endif
    scanf("%d%d", &n, &Q);
    for (int i = 1, u, v, w; i < n; i++) {
        scanf("%d%d%d", &u, &v, &w);
        g[u].emplace_back(v, w);
        g[v].emplace_back(u, w);
    }
    int p = bfs(1);
    T1.build(p), T2.build(bfs(p));
    for (int i = 1, u, k; i <= Q; i++) {
        scanf("%d%d", &u, &k);
        k = 2 * k - 1;
        printf("%lld\n", max(T1.query(u, k), T2.query(u, k)));
    }
    return 0;
}

标签:bomb,NOI,int,题解,sum,bty,ycs,include,hei
From: https://www.cnblogs.com/caijianhong/p/17742763.html

相关文章

  • 题解 P9695【[GDCPC2023] Traveling in Cells】
    显然,询问的答案即为\(x\)所在的极长的满足颜色均在\(\mathbb{A}\)内的连续段的权值和。如果我们能维护对颜色的单点修改,以及求出某个位置所在极长连续段的左右端点\(l,r\),只需要树状数组即可求出答案。一个朴素的想法是对每种颜色开一棵线段树,单点修改是平凡的,极长连续段左......
  • 题解 P9702【[GDCPC2023] Computational Geometry】
    这题一看就不是计算几何,考虑区间DP。设凸多边形的\(n\)个顶点依次为\(P_1,P_2,\cdots,P_n\)。设\(f_{i,j}\)在\(i<j\)时表示\(P_i,P_{i+1},\cdots,P_{j-1},P_j\)组成的多边形的直径的平方,在\(i>j\)时表示\(P_i,P_{i+1},\cdots,P_n,P_1,\cdots,P_{j-1},P_j\)组......
  • 题解 P9697【[GDCPC2023] Canvas】
    好题。后面的操作会覆盖前面的操作,这东西不好处理,我们不妨时光倒流,将问题转化为一个位置一旦被填了数,就再也不会变了。如果解决了这个问题,只需将操作序列倒过来,就得到了原问题的解。显然,所有\(x_i=y_i=2\)的操作会最先被执行,所有\(x_i=y_i=1\)的操作会最后被执行。只需要给......
  • 高橋君 题解
    AtCoder天下一プログラマーコンテスト2014本戦高橋君题意给定\(n,k\),求\[\sum\limits_{i=0}^{k}\dbinom{n}{i}\]多测,\(1\len,k,T\le10^5\)。题解可以考虑使用莫队求解,下文讲述如何移动指针。\(n\rightarrown+1\)根据杨辉三角递推式\(\dbinom{n}{m}=......
  • 题解 P2674 《瞿葩的数字游戏》T2-多边形数
    题目描述给你一个正整数数\(n\),问你它是不是多边形数\(K\),如果是,设\(K_1\)是最小的\(K\),\(K_2\)是次小的\(K\),输出\(K_1\)和\(K_2\)。具体思路我们主要来看上面这张表里有什么规律。性质1:\(1\)是任何一个多边形数。性质2:\(2\)不是任何一个多边形数。性......
  • 情报破译 题解
    \(d<n\le2e5,m\le10,1\lep\le10^9,0\lea_i\le1e9\)每个位运算之间独立,所以我们可以构造一个\(\{2^{k-1},2^{k-1}.....\}\)和一个\(\{0,0,0...\}\)的数组,让他们倍增去做如上运算,最后用他们把\(p\)轮拼出来,我们就知道了\(i\)位置上二进制下第\(j\)位如果是\(0\)......
  • NOIP A层联测5
    T1漂亮大厨(cook)教主的魔法+高橋君=漂亮大厨。先求出每次询问有多少个数小于等于\(y\),再统计答案。区间加,区间查小于等于某个数个数,考虑分块,块内再维护一个有序序列。区间加:散块直接加,暴力排序重构有序序列;整块打标记。区间小于等于某个数个数:散块暴力累加;整块在有序序列......
  • 国标GB28181协议下视频监控平台EasyGBS播放器起播慢或延迟高问题解决方案
    GB28181视频监控国标平台EasyGBS是基于国标GB28181协议、支持多路设备同时接入的视频监控/视频云服务平台,支持对多平台、多终端分发RTSP、RTMP、FLV、HLS、WebRTC等格式的视频流。国标GB28181平台EasyGBS可提供视频直播监控、云端录像、云存储、检索回放、智能告警、语音对讲、平......
  • P1025 [NOIP2001 提高组] 数的划分 题解
    题目传送门本题共有两种方法,分别是递归深搜和动态规划方法一:递归深搜Solution从小到大一一枚举每一个划分的数,。只要找到一种方案就记录,具体细节代码中有注释。Code#include<bits/stdc++.h>usingnamespacestd;intn,k,ans;voiddfs(intstart,intstep,intsum){......
  • 【题解】Typo
    TypoDescreption求反转一个不合法的括号序列中的一位使其成为一个合法序列的方案数(保证方案一定存在)Solution其实也就是一道较复杂的模拟题先判断哪一类括号数量更多,因为一定是将数量多的括号改成数量少的才有可能变为合法序列,这里就先用左括号举例记录每个位置时没有配对的......