Description
给定一棵n个点的树,树上有若干关键点。每次操作将一个关键点变成非关键点或一个非关键点变为关键点,并询问所有关键点形成的极小联通子树边权和的两倍。
Solution
先摆结论:
假设树上关键点按照(dfs)序排列为{(a_1,a_2,a_3,dots,a_t)}。
那么所有关键点形成的极小联通子树边权和的两倍会等于:
(dis(a_1,a_2)+dis(a_2,a_3)+dots+dis(a_{t-1},a_t)+dis(a_t,a_1))。
感性偷税发现很正确。
所以直接用一个set维护关键点按(dfs)序排列成的集合,每次加一个点x,假设其两边分别是y,z,那么答案就要加上(dis(x,y)+dis(x,z)-dis(y,z))。删掉一个点则反之。
Code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef set<int>::iterator sit;
inline int read(){//be careful for long long!
register int x=0,f=1;register char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
while(isdigit(ch)){x=x*10+(ch^'0');ch=getchar();}
return f?x:-x;
}
const int N=1e5+10;
int n,m,dfn[N],idf[N],dfc,siz[N],tp[N],dep[N],fa[N],son[N],vis[N];
ll dis[N];
struct node{int to,val;};
vector<node> E[N];
set<int> s;
inline sit pre(sit it){return --it;}
inline void Dfs_1(int nw){
dfn[nw]=++dfc,idf[dfc]=nw;
siz[nw]=1;dep[nw]=dep[fa[nw]]+1;
for(node i:E[nw])
if(i.to^fa[nw]){
fa[i.to]=nw;dis[i.to]=dis[nw]+i.val;
Dfs_1(i.to);siz[nw]+=siz[i.to];
if(siz[i.to]>siz[son[nw]])son[nw]=i.to;
}
}
inline void Dfs_2(int nw){
if(!tp[nw])tp[nw]=nw;
if(son[nw]){tp[son[nw]]=tp[nw];Dfs_2(son[nw]);}
for(node i:E[nw])
if(i.to^fa[nw]&&i.to^son[nw])
Dfs_2(i.to);
}
inline ll Getdis(int x,int y){
if(!x||!y)return 0;
ll ans=0;
while(tp[x]^tp[y]){
if(dep[tp[x]]>=dep[tp[y]])ans+=dis[x]-dis[fa[tp[x]]],x=fa[tp[x]];
else ans+=dis[y]-dis[fa[tp[y]]],y=fa[tp[y]];
}
ans+=abs(dis[x]-dis[y]);
return ans;
}
int main(){
n=read(),m=read();
for(int t=1;t<n;++t){
int x=read(),y=read(),z=read();
E[x].push_back((node){y,z});E[y].push_back((node){x,z});
}
Dfs_1(1);Dfs_2(1);
ll ans=0;
for(int i=1;i<=m;++i){
int x=dfn[read()];
if(!s.size())vis[idf[x]]=1,s.insert(x);
else{
sit it=s.lower_bound(x);
int y=idf[*(it=(it==s.begin()?pre(s.end()):pre(it)))];
it=s.upper_bound(x);
int z=idf[*(it=(it==s.end()?s.begin():it))];
x=idf[x];
ll d=Getdis(x,y)+Getdis(x,z)-Getdis(y,z);
if(!vis[x])ans+=d,s.insert(dfn[x]);
else ans-=d,s.erase(s.find(dfn[x]));
vis[x]^=1;
}
printf("%lld
",ans);
}
return 0;
}