首页 > 其他分享 >虚树 学习笔记

虚树 学习笔记

时间:2023-02-01 18:34:35浏览次数:66  
标签:int top 笔记 学习 dfn lca include 虚树 关键点

虚树 学习笔记

如果有这么一个问题

在一棵超大的,有 \(n\) 个节点树上,并且树上有 \(m\) 个关键点,\(m\) 远小于 \(n\),如果问题只与关键点有关,我们不能很方便地在这棵超大的树上处理问题,考虑减少冗余。

image

对于这样的一棵树(关键点用红色标注出来了),考虑它的节点中有哪些点对答案有真正的影响。

首先关键点肯定要保留下来,如果只保留关键点,不能很好地表达点之间的关系,为了解决这个问题,可以把关键点之间的 \(\text{LCA}\) 一并保留下来,得到一棵这样的树:

image

其中蓝色点是关键点的 \(\text{LCAs}\)。

对于这样一棵树,我们称之为虚树,它在保留关键点的祖孙后代关系的前提下,整棵树的点数 \(\le 2m\) 。

构建虚树

如果枚举所有的点对,分别求 \(\text{LCA}\) 这样的时间复杂度显然是平方级别的,不可接受。

可以采用一种名为 Increamental Algorithm 的思想,译为增量算法,在这里的应用类似笛卡尔树的构建,都是维护一条最右链(单调栈)。

把关键点按 \(dfn\) 排序,对于刚刚的例子,得到 4 5 10 7

为了方便,先把 \(1\) 插入单调栈内,接着遍历排序后的数组,进行如下操作:

  1. 假设当前遍历到 \(u\),取出栈顶元素 \(top\),记栈顶底下的元素 \(stk[top - 1]\) 为 \(top - 1\),计算 \(lca = \text{LCA(u, top)}\)。
  2. 判断 \(dfn[lca]\) 与 \(dfn[top]\),如果 \(dfn[lca] = dfn[top]\),意味着 \(lca\) 就是栈顶元素,此时 \(u\) 与栈内元素在同一条链上,不用理这种情况。

image

  1. 如果 \(dfn[lca] < dfn[top - 1]\),那么不断弹出栈顶(顺便连接 \(top - 1\) 与 \(top\),表示这两点一定在虚树内), \(top\) 与 \(top-1\) 均发生改变,此时有两种情况,图为原树中的子孙关系。

image

  • \(lca \ne top - 1(dfn[lca] > dfn[top -1])\),如下图,此时把 \(top\) 连带着它的儿子们扔到 \(lca\) 左子树中,并把 \(lca\) 代替 \(top\) 成为栈顶。

image

  • \(lca = top -1(dfn[lca] = dfn[top - 1])\),类似地,只不过不用把 \(lca\) 压入栈里了。

image

对于上述所有情况,都要记得把 \(u\) 放进最右链(单调栈)。

最后把栈内剩余的元素全部放进虚树(\(top - 1\) 向 \(top\) 连一条边)。

这样就完成了虚树的构建,除了排序的部分,都是线性时间复杂度的。

建树之后

对于 [SDOI2011] 消耗战,有一个显然的树形dp:

\(dp[i]\) 表示以 \(i\) 为根的子树中,删掉所有关键点所需的最小花费。

预处理 \(minv[i]\),表示 \(i\) 到 \(1\) 的最短边长。

  1. \(i\) 是关键点,\(dp[i] = minv[i]\)
  2. \(i\) 不是关键点,\(dp[i] = min(minv[i], \sum_{(i, v, w)\in E}w_i)\)

在虚树上跑一遍 dp 就好了,如果用 RMQ ST 实现 LCA 时间复杂度:\(O(n\log n + \sum (k\log k))\)

// Problem: P2495 [SDOI2011] 消耗战
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2495
// Memory Limit: 505 MB
// Time Limit: 2000 ms
// Author: Moyou
// Copyright (c) 2022 Moyou All rights reserved.
// Date: 2023-01-31 18:13:24

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <iostream>
#include <map>
#include <queue>
#include <stack>
#include <tuple>
#include <unordered_map>
#define x first
#define y second
#define int long long
#define speedup (ios::sync_with_stdio(0), cin.tie(0), cout.tie(0))
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

const int N = 2e5 + 10;

vector<PII> g[N];

struct STLCA
{
    int pos[N], idx, dfn[(N << 1) + 10];
    int st[21][(N << 1) + 10], lg[(N << 1) + 10];
    void dfs(int p, int fa)
    {
        dfn[++idx] = p;
        pos[p] = idx;
        for (auto [v, w] : g[p])
        {
            if (v == fa)
                continue;
            dfs(v, p);
            dfn[++idx] = p;
        }
    }

    int Min(int a, int b)
    {
        return pos[a] < pos[b] ? a : b;
    }
    void ST()
    {
        lg[1] = 0;
        for (int i = 2; i <= (N << 1); i++)
            lg[i] = lg[i >> 1] + 1;
        for (int i = 1; i <= (N << 1); i++)
            st[0][i] = dfn[i];
        for (int i = 1; i <= lg[(N << 1) - 1]; i++)
            for (int j = 1; j + (1 << i) <= (N << 1); j++)
                st[i][j] = Min(st[i - 1][j], st[i - 1][j + (1 << i - 1)]);
    }

    int LCA(int u, int v)
    {
        int l = pos[u], r = pos[v];
        if (l > r)
            swap(l, r);
        int k = lg[r - l + 1];
        return Min(st[k][l], st[k][r - (1 << k) + 1]);
    }
} st;

int dp[N], dfn[N], cnt;
int imp[N];
bool is[N];
int k;
int minv[N];

void dfs(int u, int fa)
{
    dfn[u] = ++cnt;
    for (auto [v, w] : g[u])
    {
        if (v == fa)
            continue;
        minv[v] = min(minv[u], w);
        dfs(v, u);
    }
}

vector<int> vir[N];

void add(int a, int b)
{
    vir[a].push_back(b);
}

int Dp(int u, int fa)
{
    int sum = 0;
    for (auto v : vir[u])
        sum += Dp(v, u);
    int ans = INF;
    vir[u].clear();
    if(is[u]) 
        ans = minv[u];
    else ans = min(minv[u], sum);
    is[u] = false;
    return ans;
}

int n;
int bucket[N];
void build()
{
    sort(imp + 1, imp + k + 1, [](int a, int b) { return dfn[a] < dfn[b]; });
    int stk[N], top = 0;
    stk[++top] = 1;
    for (int i = 1; i <= k; i++)
    {
        if (imp[i] == 1)
            continue;
        int lca = st.LCA(stk[top], imp[i]);
        if(lca != stk[top])
        {
            while (dfn[lca] < dfn[stk[top - 1]])
                add(stk[top - 1], stk[top]), top --;
            if (lca != stk[top - 1])
                add(lca, stk[top]), stk[top] = lca;
            else
                add(lca, stk[top--]);
        }
        stk[++top] = imp[i];
    }

    for (int i = 1; i < top; i++)
        add(stk[i], stk[i + 1]);

}

signed main()
{
    speedup;
    cin >> n;
    memset(minv, 0x7f, sizeof minv);
    for (int i = 1; i < n; i++)
    {
        int a, b, c;
        cin >> a >> b >> c;
        g[a].push_back({b, c}), g[b].push_back({a, c});
    }

    dfs(1, 0);
    st.dfs(1, 0);
    st.ST();

    int m;
    cin >> m;
    for (int i = 1; i <= m; i++)
    {
        cin >> k;
        for (int j = 1; j <= k; j++)
            cin >> imp[j], is[imp[j]] = true;
        build();
        cout << Dp(1, 0) << "\n";
    }

    return 0;
}

标签:int,top,笔记,学习,dfn,lca,include,虚树,关键点
From: https://www.cnblogs.com/MoyouSayuki/p/17083830.html

相关文章

  • Mysql学习笔记
    Mysql是关系型数据库管理系统,管理的数据库是一堆关联表的集合。这里的表可以看作是一个二维表格,里面的每一行表示一条记录,是一组相关的数据。每一列存储的是一个属性对应的......
  • 建筑行业VR安全体验,亲身感受事故危害,学习安全技能
    建筑安全VR体验教育是一种利用虚拟现实技术来提高工地安全意识的新型安全教育方式,它不仅可以让工人们在没有实际危险的情况下学习安全技能,而且还能让他们体验到真实的安全......
  • Java基础学习09
    今天简单做小系统,之前也做过的类似的系统,想重新复习一次逻辑业务(2023-02-01-16:10:49)这次学到有了一个小的函数//获取本地时间并将时间格式化,调用sdf.format(date)输出......
  • 随堂笔记3-spring之底层架构核心概念解析
    1.BeanDefinition:bean定义,有一些特定属性描述bean,比如bean类型-class,scope作用域,lazyInit是否懒加载2.beanDefinitionReader:beanDefinition读取器,比如AnnotationBeanDe......
  • 2023年JS学习记录
    2023/1/30星期一https://blog.csdn.net/Augenstern_QXL/article/details/119249534短路运算(逻辑中断)短路运算的原理:当有多个表达式(值)时,左边的表达式值可以确定结果时......
  • ElasticSearch 学习笔记
    ElasticSearch基础知识索引index一个索引就是一个拥有几分相似特征的文档的集合。索引就类似于关系型数据库中的库的概念。类型type一个类型是索引中的一......
  • 学习bash反弹shell过程中所想到的
       bash-i>&/dev/tcp/ip/port0>&1   在这一句命令中,主要包含两个问题:“>&”和“/dev/tcp/ip/port”。1. /dev/tcp/ip/port  /dev目录下存放这设备文......
  • markdown入门学习
    1、标题(1)一级标题:#演示:一级标题一级标题的另一种写法:下一行写===演示:一级标题的另一种写法(2)二级标题:##演示:二级标题二级标题的另一种写法:下一行写---演示:二级......
  • HTTP学习笔记3-HTTP报文
    HTTP协议主要由三大部分组成:起始行(startline):描述请求或响应的基本信息;头部字段(header):使用key-value形式更详细地说明报文;消息正文(entity):实际传输的数据,它不一定是纯文......
  • [网络同步] < 网络同步在游戏历史中的发展变化> 阅读笔记
    最近阅读: 网络同步在游戏历史中的发展变化  https://mp.weixin.qq.com/s/9Nghv8O9HXJFVf6L1m9wpg  学到不少,大致对游戏网络有了大方向的了解,做个记录。 1.帧同步......