考虑关键点(有宝藏的点)及其lca构成的虚树,由于最后还需要回到原点,因此答案相当于虚树中所有边权的和的两倍。
考虑树的边权的两倍怎么求,实际上就是按dfs序排序之后第一个点和第二个点,第二个点和第三个点……最后一个点和第一个点的距离的和。那么用set维护dfs序,插入和删除的时候统计一下就好了。
AC代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<set> #define N 100005 #define inf 1000000000 #define ll long long using namespace std; set<int> s; int n,m,tot,dfsclk,dep[N],fst[N],pnt[N<<1],nxt[N<<1],pos[N],id[N],fa[N][17],a[N],bin[25]; ll d[N],len[N<<1]; int read(){ int x=0; char ch=getchar(); while (ch<'0' || ch>'9') ch=getchar(); while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); } return x; } void add(int x,int y,ll z){ pnt[++tot]=y; len[tot]=z; nxt[tot]=fst[x]; fst[x]=tot; } void dfs(int x){ pos[x]=++dfsclk; id[dfsclk]=x; int p,i; for (i=1; bin[i]<=dep[x]; i++) fa[x][i]=fa[fa[x][i-1]][i-1]; for (p=fst[x]; p; p=nxt[p]){ int y=pnt[p]; if (y!=fa[x][0]){ fa[y][0]=x; d[y]=d[x]+len[p]; dep[y]=dep[x]+1; dfs(y); } } } int lca(int x,int y){ if (dep[x]<dep[y]) swap(x,y); int tmp=dep[x]-dep[y],i; for (i=0; bin[i]<=tmp; i++) if (tmp&bin[i]) x=fa[x][i]; for (i=16; i>=0; i--) if (fa[x][i]!=fa[y][i]){ x=fa[x][i]; y=fa[y][i]; } return (x==y)?x:fa[x][0]; } ll dist(int x,int y){ return d[x]+d[y]-(d[lca(x,y)]<<1); } int main(){ n=read(); m=read(); int i; bin[0]=1; for (i=1; i<=17; i++) bin[i]=bin[i-1]<<1; for (i=1; i<n; i++){ int x=read(),y=read(),z=read(); add(x,y,(ll)z); add(y,x,(ll)z); } dfs(1); ll ans=0,tmp; s.insert(-inf); s.insert(inf); set<int>::iterator it; for (i=1; i<=m; i++){ int k=read(),t; if (a[k]){ s.erase(pos[k]); t=-1; } else{ s.insert(pos[k]); t=1; } a[k]^=1; it=s.upper_bound(pos[k]); int r=*it,l=*(--it); if (l>=pos[k]) l=*(--it); if (l!=-inf) ans+=dist(id[l],k)*t; if (r!=inf) ans+=dist(id[r],k)*t; if (l!=-inf && r!=inf) ans-=dist(id[l],id[r])*t; tmp=(s.size()>3)?dist(id[*s.upper_bound(-inf)],id[*--s.lower_bound(inf)]):0; printf("%lld\n",ans+tmp); } return 0; }
by lych
2016.3.6