我们找到了这样一个结论:
「一个树上点集构成的最小生成树中,若两点间有路径,则此两点的DFS序在树上相近。」
这个结论的证明是较为困难的,但是感性理解还是比较容易的。
有了这个结论,我们就可以解决这一题。
每当一个新的点x即将加入点集的时候,我们只需要计算点集中DFS序刚好比它小的点y和DFS序刚好比它大的点z,然后将答案加上如下的式子:
$$dis_{x,y}+dis_{x,z}-dis_{y,z}$$
删去的时候逆向操作即可。
维护点集可以使用set。(善用STL(大雾))计算路径长度可以LCA+容斥。
注意:记得开long long
#include<iostream>
#include<cstdio>
#include<set>
struct ee{
int v;
int w;
int nxt;
}e[200005];
int h[100005],et=0;
inline void add(int U,int V,int W){
e[++et]=(ee){V,W,h[U]};
h[U]=et;
}
int fa[100005][30],dfn[100005],loc[100005],dep[100005],cnt=0;
long long val[100005];
//注意将两种深度分开。
inline void dfs(int X,int FA){
fa[X][0]=FA,dfn[X]=++cnt,loc[cnt]=X,dep[X]=dep[FA]+1;
for(int i=h[X];i;i=e[i].nxt){
if(e[i].v!=FA){
val[e[i].v]=val[X]+e[i].w;
dfs(e[i].v,X);
}
}
}
inline int lca(int X,int Y){
if(dep[X]<dep[Y]){
std::swap(X,Y);
}
for(int i=20;i>=0;--i){
if(dep[X]-(1<<i)>=dep[Y]){
X=fa[X][i];
}
}
if(X==Y){
return X;
}
for(int i=20;i>=0;--i){
if(fa[X][i]!=fa[Y][i]){
X=fa[X][i];
Y=fa[Y][i];
}
}
return fa[X][0];
}
inline long long dis(int X,int Y){
return val[X]+val[Y]-(val[lca(X,Y)]<<1);
}
int n,m;
long long ans=0;
bool vis[100005];
std::set<int> s;
void init(){
scanf("%d%d",&n,&m);
int u,v,w;
for(int i=1;i<n;++i){
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
dfs(1,0);
for(int j=1;j<=20;++j){
for(int i=1;i<=n;++i){
fa[i][j]=fa[fa[i][j-1]][j-1];
}
}
int x,y,z;
long long d;
std::set<int>::iterator it;
for(int i=1;i<=m;++i){
scanf("%d",&x);
x=dfn[x];
if(!vis[loc[x]]){
s.insert(x);
}
y=loc[(it=s.lower_bound(x))==s.begin()?*--s.end():*--it];
z=loc[(it=s.upper_bound(x))==s.end()?*s.begin():*it];
//注意运算符优先级。
if(vis[loc[x]]){
s.erase(x);
}
x=loc[x];
d=dis(x,y)+dis(x,z)-dis(y,z);
if(!vis[x]){
vis[x]=1;
ans+=d;
}else{
vis[x]=0;
ans-=d;
}
//注意前后的x不是一个x
printf("%lld\n",ans);
}
}
int main(){
init();
return 0;
}