「学习笔记」虚树

版权声明:本文为hzy原创文章,未经博主允许不可随意转载。 https://blog.csdn.net/Binary_Heap/article/details/82530955

虚树就是一棵树上选一些点,它们之间形成的树形路径就是虚树。

虚树也就是把一些与操作无关的点略去,节省复杂度

直接上例题~

[SDOI2015] 寻宝游戏

题目传送门

可以发现是这些有宝藏的关键点形成的树,路径权值和的两倍。

我们可以按dfs序从小到大走一遍,就是答案了

定义dfs序上的前驱指的是dfs比它小的点中dfs序最大的点。如果没有,就是dfs序最大的那个点(尾部)。后继同理。

考虑一个点加进来对答案的影响,设其前驱为 p r e ,后继为 a f t e r ,则答案减少 d i s t ( p r e , a f t e r ) ,加上 d i s t ( p r e , u ) + d i s t ( u , a f t e r )

使用 s e t 完成这一过程。

#include <iostream>
#include <cstdio>
#include <vector>
#include <set>
using namespace std;

typedef pair<int, int> P;
typedef long long LL;

const int MAXN = 1e5 + 10;

int n, q;
vector<P> G[MAXN];

int top[MAXN], pos[MAXN], dfn[MAXN], dfn_c;
int fa[MAXN], sz[MAXN], deep[MAXN], son[MAXN];
LL dep[MAXN];

void dfs1(int u, int f, LL d) {
    pos[ dfn[u] = ++ dfn_c ] = u;
    fa[u] = f, dep[u] = d, sz[u] = 1;
    for(int i = 0, v; i < G[u].size(); i ++) {
        v = G[u][i].first;
        if(v != f) {
            deep[v] = deep[u] + 1;
            dfs1(v, u, d + G[u][i].second);
            sz[u] += sz[v];
            if(sz[v] > sz[ son[u] ]) son[u] = v;
        }
    }
}

void dfs2(int u, int topf) {
    top[u] = topf;
    if(!son[u]) return ;
    dfs2(son[u], topf);
    for(int i = 0, v; i < G[u].size(); i ++)
        if(!top[v = G[u][i].first]) dfs2(v, v);
}

int LCA(int u, int v) {
    for(; top[u] != top[v]; u = fa[ top[u] ])
        if(deep[ top[u] ] < deep[ top[v] ]) swap(u, v);
    if(deep[u] > deep[v]) swap(u, v);
    return u;
}

LL dist(int u, int v) {
    return dep[u] + dep[v] - 2ll * dep[LCA(u, v)];
}

set<int> Dfn;
LL ans(0);

LL calc(int x) {
    if(Dfn.empty()) return Dfn.insert(dfn[x]), 0;
    LL tag = Dfn.count(dfn[x]) > 0 ? -1LL : 1LL;
    if(tag > 0) Dfn.insert(dfn[x]);
    set<int> :: iterator i = Dfn.lower_bound(dfn[x]), pre = i, after = i;
    pre --;
    after ++;

    if(*i == *(Dfn.begin())) pre = Dfn.end(), pre --;
    if(after == Dfn.end()) after = Dfn.begin();

    ans -= tag * dist(pos[*after], pos[*pre]);
    ans += tag * dist(pos[*after], x);
    ans += tag * dist(pos[*pre], x);
    if(tag == -1LL) Dfn.erase(dfn[x]);
    return ans;
}

int main() {
    scanf("%d%d", &n, &q);
    for(int i = 1, u, v, w; i < n; i ++) {
        scanf("%d%d%d", &u, &v, &w);
        G[u].push_back(P(v, w));
        G[v].push_back(P(u, w));
    }
    dfs1(1, 0, 0);
    dfs2(1, 1);
    for(int i = 1, x; i <= q; i ++) {
        scanf("%d", &x);
        printf("%lld\n", calc(x));
    }
    return 0;
}

[SDOI2011]消耗战

待填坑。

猜你喜欢

转载自blog.csdn.net/Binary_Heap/article/details/82530955