【BZOJ】3991: [SDOI2015]寻宝游戏
2015-11-22 17:53
225 查看
题意
给一个\(n\)个点带边权的树。有\(m\)次操作,每一次操作一个点\(x\),如果\(x\)已经出现,则\(x\)消失。否则\(x\)出现。每一操作后,询问从某个点开始走,直到经过所有出现的点,最后再回到开始的那个点的最短路程。分析
首先容易知道我们选任意一个在某两点路径上的点作为起点都能得到最优解(包括端点)。我们只需要考虑走的顺序。题解
由于按照dfs序的走法是最短的,因此我们按dfs序维护一下前前后后的距离和即可。如何证明?好像并不会...#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=100005; int n, ihead , FF , dep , cnt, a , fa [17], m; ll d ; struct E { int next, to, w; }e[N<<1]; struct dat { int FF, id; ll dis; bool operator < (const dat &a) const { return FF<a.FF; } }; set<dat> s; void add(int x, int y, int w) { e[++cnt]=(E){ihead[x], y, w}; ihead[x]=cnt; e[++cnt]=(E){ihead[y], x, w}; ihead[y]=cnt; } void dfs(int x, int f=0) { static int fid=0; FF[x]=++fid; for(int i=1; i<=16; ++i) { fa[x][i]=fa[fa[x][i-1]][i-1]; } for(int i=ihead[x]; i; i=e[i].next) { int y=e[i].to; if(y==f) { continue; } fa[y][0]=x; dep[y]=dep[x]+1; d[y]=d[x]+e[i].w; dfs(y, x); } } int LCA(int x, int y) { if(dep[x]<dep[y]) { swap(x, y); } int d=dep[x]-dep[y]; for(int i=16; i>=0; --i) if((d>>i)&1) x=fa[x][i]; if(x==y) return x; for(int i=16; i>=0; --i) if(fa[x][i]!=fa[y][i]) x=fa[x][i], y=fa[y][i]; return fa[x][0]; } ll getdis(int x, int y) { int lca=LCA(x, y); return d[x]+d[y]-(d[lca]<<1); } int main() { scanf("%d%d", &n, &m); for(int i=1; i<n; ++i) { int x, y, w; scanf("%d%d%d", &x, &y, &w); add(x, y, w); } dfs((n+1)>>1); ll ans=0; while(m--) { int p; scanf("%d", &p); dat t; if(a[p]) { set<dat>::iterator it=s.lower_bound((dat){FF[p], 0, 0}), itp=s.end(), itb=s.end(); itb=it; ++itb; ans-=it->dis; if(it!=s.begin()) { itp=it; --itp; } if(itb!=s.end()) { ans-=itb->dis; if(itp==s.end()) { t=*itb; t.dis=0; s.erase(itb); s.insert(t); } else { t=*itb; t.dis=getdis(itb->id, itp->id); ans+=t.dis; s.erase(itb); s.insert(t); } } s.erase(it); a[p]=0; } else { set<dat>::iterator it=s.lower_bound((dat){FF[p], 0, 0}), itp=s.end(); if(it!=s.begin()) { itp=it; --itp; } if(it!=s.end()) { if(itp!=s.end()) { ll dis=getdis(p, itp->id); s.insert((dat){FF[p], p, dis}); ans+=dis; } else { s.insert((dat){FF[p], p, 0}); } t=*it; ans-=t.dis; t.dis=getdis(p, it->id); ans+=t.dis; s.erase(it); s.insert(t); } else { if(itp==s.end()) { s.insert((dat){FF[p], p, 0}); } else { ll dis=getdis(p, itp->id); s.insert((dat){FF[p], p, dis}); ans+=dis; } } a[p]=1; } ll temp=0; if(s.size()>=2) { set<dat>::iterator it=s.end(); --it; temp=getdis(s.begin()->id, it->id); } printf("%lld\n", ans+temp); } return 0; }
相关文章推荐
- 排序算法的复杂度
- iOS---限制文本框输入的长度
- redis linux安装与简单集群配置
- 【BZOJ】3930: [CQOI2015]选数
- 11.22没精打采的一天
- Android开发模式:模型—视图—主导器模式
- 深入浅出UML类图(一)
- C 中 关于printf 函数中度剖析
- 1.1编程基础之输入输出
- mac系统使用lookup file.vim的问题
- UML类图实例
- UML类图几种关系的总结
- state thread
- linux下tar.gz、tar、bz2、zip等解压缩、压缩命令小结
- 【BZOJ】3922: Karin的弹幕
- k路归并算法的分析和实现
- zzulioj 1780: 和尚特烦恼6——炒股
- OpenGL2 CoreImage初探与Model封装
- 家庭作业汇总
- 一个设置访问权限的实例