虚树就是一棵树上选一些点,它们之间形成的树形路径就是虚树。
虚树也就是把一些与操作无关的点略去,节省复杂度
直接上例题~
[SDOI2015] 寻宝游戏
可以发现是这些有宝藏的关键点形成的树,路径权值和的两倍。
我们可以按dfs序从小到大走一遍,就是答案了
定义dfs序上的前驱指的是dfs比它小的点中dfs序最大的点。如果没有,就是dfs序最大的那个点(尾部)。后继同理。
考虑一个点加进来对答案的影响,设其前驱为 pre p r e ,后继为 after a f t e r ,则答案减少 dist(pre,after) d i s t ( p r e , a f t e r ) ,加上 dist(pre,u)+dist(u,after) d i s t ( p r e , u ) + d i s t ( u , a f t e r ) 。
使用 set 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]消耗战
待填坑。