[SDOI2015]寻宝游戏

Link

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\n",ans);
    }
    return 0;
}

 

posted @ 2019-12-22 16:57  Fruitea  阅读(215)  评论(0编辑  收藏  举报