题意:
小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达。游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路上行走,若走到某个村庄中有宝物,则视为找到该村庄内的宝物,直到找到所有宝物并返回到最初转移到的村庄为止。小B希望评测一下这个游戏的难度,因此他需要知道玩家找到所有宝物需要行走的最短路程。但是这个游戏中宝物经常变化,有时某个村庄中会突然出现宝物,有时某个村庄内的宝物会突然消失,因此小B需要不断地更新数据,但是小B太懒了,不愿意自己计算,因此他向你求助。为了简化问题,我们认为最开始时所有村庄内均没有宝物
题解:
①有一个比较常见的结论:
从一个点出发,经过指定的k个点再回到原点走的路径的长度是这k个点构成的生成树的边权和的两倍
②这道题如果把有宝藏的点当作特殊点,就相当于维护m棵虚树.
③每次重新建虚树是肯定不行的,每次只会修改一个点,插入和删除这个点一定会伴随着若干条路径的改变,找到这些路径就好了
④画图分析可以知道:
这些路径一定是当前操作的点x和DFS序在它前面的最后一个点L组成的
或者是和DFS序在它后面的第一个点R组成的
⑴如果L存在,那么这条路径就是L→x
⑵如果R存在路径R→x到x也会受到影响
⑶如果L和R同时存在,还要排除L→R的影响
⑷ans还要加上tmp是因为直接加dis会使DFS序最小的点x到DFS序最大的点y这条边x→y只算了一次,要加上另一次
⑤只需要记住每次加点还是删点都会改变若干条路径对答案的贡献,找到这些路径并修改贡献就好了.
⑥因为要每次要插入一个DFS序,找前驱后继,所以用set来维护
#include <cstdio> #include <cstring> #include <algorithm> #include <set> using namespace std; typedef long long LL; const int maxn=100000+10; struct Node{ int to,next,data; }a[maxn<<1]; int n,m,head[maxn],cnt; int prt[maxn],deep[maxn],size[maxn],son[maxn]; LL dis[maxn]; int Dfn[maxn],Time,top[maxn],pos[maxn]; bool flag[maxn]; LL ans=0; set<int>S; void Init(); void Insert(int,int,int); void DFS1(int,int,int); void DFS2(int,int); int LCA(int,int); LL Dis(int,int); void Solve_Insert(int); void Solve_Delte(int); int main(){ // freopen("in.cpp","r",stdin); Init(); return 0; } void Init(){ scanf("%d%d",&n,&m); int x,y,z; for(int i=1;i<n;i++){ scanf("%d%d%d",&x,&y,&z); Insert(x,y,z);Insert(y,x,z); } DFS1(1,0,1); DFS2(1,1); S.insert(0);S.insert(n+1); while(m--){ scanf("%d",&x); flag[x]=!flag[x]; if(flag[x])Solve_Insert(x); else Solve_Delte(x); } } void Solve_Insert(int x){ int L,R,prt1,prt2;LL tmp=0; S.insert(Dfn[x]); L=*--S.find(Dfn[x]);R=*++S.find(Dfn[x]); if(L>=1)ans+=Dis(pos[L],x); if(R<=n)ans+=Dis(pos[R],x); if(L>=1 && R<=n)ans-=Dis(pos[L],pos[R]); L=*++S.find(0),R=*--S.find(n+1); if(L>=1 && R<=n) tmp=Dis(pos[L],pos[R]); printf("%lld ",tmp+ans); } void Solve_Delte(int x){ int L,R,prt1,prt2;LL tmp=0; L=*--S.find(Dfn[x]);R=*++S.find(Dfn[x]); if(L>=1)ans-=Dis(pos[L],x); if(R<=n)ans-=Dis(pos[R],x); if(L>=1 && R<=n)ans+=Dis(pos[L],pos[R]); S.erase(Dfn[x]); L=*++S.find(0),R=*--S.find(n+1); if(L>=1 && R<=n) tmp=Dis(pos[L],pos[R]); printf("%lld ",tmp+ans); } void Insert(int x,int y,int z){ a[++cnt].to=y; a[cnt].next=head[x]; a[cnt].data=z; head[x]=cnt; } #define y a[i].to void DFS1(int x,int prt,int deep){ ::deep[x]=deep; ::prt[x]=prt; ::size[x]=1; for(int i=head[x];i;i=a[i].next){ if(y==prt)continue; dis[y]=dis[x]+a[i].data; DFS1(y,x,deep+1); size[x]+=size[y]; if(size[y]>size[son[x]])son[x]=y; } } void DFS2(int x,int top){ ::Dfn[x]=++Time; ::top[x]=top; ::pos[Time]=x; if(son[x])DFS2(son[x],top); for(int i=head[x];i;i=a[i].next){ if(y==prt[x] || y==son[x])continue; DFS2(y,y); } } #undef y int LCA(int x,int y){ while(top[x]!=top[y]){ if(deep[top[x]]<deep[top[y]])swap(x,y); x=prt[top[x]]; } if(deep[x]<deep[y])return x; return y; } LL Dis(int x,int y){ return dis[x]+dis[y]-2*dis[LCA(x,y)]; }