传送门

题意:

$n$个点的树,$m$次变动使得某个点有宝物或没宝物,询问每次变动后集齐所有宝物并返回原点的最小距离


转化成有根树,求树链的并...

两两树链求并就可以,但我们按照$dfs$序来两两求并,相邻两点深度和减去$lca$的深度

一次只变动一个关键点,用$set$动态维护虚树($dfs$序)就好了

并不需要建树

注意树根不是$1$,但也不能每次减当前树根的深度,所以我们放在最后让树链的并减去树根的深度

【15:02:56】学到一个trick,可以用一个仿函数比较$dfs$序传到$set$里,很方便

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;
const int N=1e5+;
inline int read(){
char c=getchar();int x=,f=;
while(c<''||c>''){if(c=='-')f=-;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
return x*f;
} int n,Q,u,v,x;
struct Edge{
int v,ne,w;
}e[N<<];
int cnt,h[N];
inline void ins(int u,int v,int w){
cnt++;
e[cnt].v=v;e[cnt].w=w;e[cnt].ne=h[u];h[u]=cnt;
cnt++;
e[cnt].v=u;e[cnt].w=w;e[cnt].ne=h[v];h[v]=cnt;
}
int dfn[N],dfc,top[N],size[N],mx[N],deep[N],fa[N];
int pos[N];
ll dis[N];
void dfs(int u){
size[u]++;
for(int i=h[u];i;i=e[i].ne){
int v=e[i].v;
if(v==fa[u]) continue;
fa[v]=u;deep[v]=deep[u]+;
dis[v]=dis[u]+e[i].w;
dfs(v);
size[u]+=size[v];
if(size[v]>size[mx[u]]) mx[u]=v;
}
}
void dfs2(int u,int anc){
dfn[u]=++dfc;top[u]=anc;
pos[dfc]=u;
if(mx[u]) dfs2(mx[u],anc);
for(int i=h[u];i;i=e[i].ne)
if(e[i].v!=fa[u] && e[i].v!=mx[u]) dfs2(e[i].v,e[i].v);
}
inline int lca(int x,int y){
if(x== || y==) return ;
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]]) swap(x,y);
x=fa[top[x]];
}
return deep[x]<deep[y] ? x : y;
} set<int> S;
set<int>::iterator it;
int has[N],root;
ll chain;
inline void ModifyRoot(){
it=S.end();
root=lca(pos[*S.begin()],pos[*(--it)]);
}
void vtrAdd(int x){//printf("Add %d %d\n",x,dfn[x]);
it=S.insert(dfn[x]).first; chain+=dis[x];
int a=,b=;
if( it!=S.begin() ) a=pos[*(--it)++];
if( (++it)!=S.end() ) b=pos[*it];
//printf("a b %d %d\n",a,b);
chain+=dis[ lca(a,b) ];
chain-=dis[ lca(a,x) ] + dis[ lca(b,x) ];
}
void vtrDel(int x){//printf("Del %d %d\n",x,dfn[x]);
chain-=dis[x];
it=S.find(dfn[x]);
int a=,b=;
if(it!=S.begin()) a=pos[*(--it)++];
if((++it)!=S.end()) b=pos[*it];
chain+=dis[ lca(a,x) ] + dis[ lca(b,x) ];
chain-=dis[ lca(a,b) ];
S.erase(dfn[x]);
ModifyRoot();
} int main(){
freopen("in","r",stdin);
n=read();Q=read();
for(int i=;i<n;i++) u=read(),v=read(),ins(u,v,read());
dfs();dfs2(,);
//for(int i=1;i<=n;i++) printf("%d ",dfn[i]);puts("");
while(Q--){
x=read(); has[x]^=;
if(has[x]) vtrAdd(x);
else vtrDel(x);
ModifyRoot();
printf("%lld\n",(chain-dis[root])<<);
}
}
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;
const int N=1e5+;
inline int read(){
char c=getchar();int x=,f=;
while(c<''||c>''){if(c=='-')f=-;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
return x*f;
} int n,Q,u,v,x;
struct Edge{
int v,ne,w;
}e[N<<];
int cnt,h[N];
inline void ins(int u,int v,int w){
cnt++;
e[cnt].v=v;e[cnt].w=w;e[cnt].ne=h[u];h[u]=cnt;
cnt++;
e[cnt].v=u;e[cnt].w=w;e[cnt].ne=h[v];h[v]=cnt;
}
int dfn[N],dfc,top[N],size[N],mx[N],deep[N],fa[N];
ll dis[N];
void dfs(int u){
size[u]++;
for(int i=h[u];i;i=e[i].ne){
int v=e[i].v;
if(v==fa[u]) continue;
fa[v]=u;deep[v]=deep[u]+;
dis[v]=dis[u]+e[i].w;
dfs(v);
size[u]+=size[v];
if(size[v]>size[mx[u]]) mx[u]=v;
}
}
void dfs2(int u,int anc){
dfn[u]=++dfc;top[u]=anc;
if(mx[u]) dfs2(mx[u],anc);
for(int i=h[u];i;i=e[i].ne)
if(e[i].v!=fa[u] && e[i].v!=mx[u]) dfs2(e[i].v,e[i].v);
}
inline int lca(int x,int y){
if(x== || y==) return ;
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]]) swap(x,y);
x=fa[top[x]];
}
return deep[x]<deep[y] ? x : y;
}
struct DFSN{
bool operator ()(int x,int y){return dfn[x]<dfn[y];}
};
set<int,DFSN> S;
set<int,DFSN>::iterator it;
int has[N],root;
ll chain;
inline void ModifyRoot(){
it=S.end();
root=lca(*S.begin(),*(--it));
}
void vtrAdd(int x){//printf("Add %d %d\n",x,dfn[x]);
it=S.insert(x).first; chain+=dis[x];
int a=,b=;
if( it!=S.begin() ) a=*(--it)++;
if( (++it)!=S.end() ) b=*it;
//printf("a b %d %d\n",a,b);
chain+=dis[ lca(a,b) ];
chain-=dis[ lca(a,x) ] + dis[ lca(b,x) ];
}
void vtrDel(int x){//printf("Del %d %d\n",x,dfn[x]);
chain-=dis[x];
it=S.find(x);
int a=,b=;
if(it!=S.begin()) a=*(--it)++;
if((++it)!=S.end()) b=*it;
chain+=dis[ lca(a,x) ] + dis[ lca(b,x) ];
chain-=dis[ lca(a,b) ];
S.erase(x);
ModifyRoot();
} int main(){
freopen("in","r",stdin);
n=read();Q=read();
for(int i=;i<n;i++) u=read(),v=read(),ins(u,v,read());
dfs();dfs2(,);
//for(int i=1;i<=n;i++) printf("%d ",dfn[i]);puts("");
while(Q--){
x=read(); has[x]^=;
if(has[x]) vtrAdd(x);
else vtrDel(x);
ModifyRoot();
printf("%lld\n",(chain-dis[root])<<);
}
}

BZOJ 3991: [SDOI2015]寻宝游戏 [虚树 树链的并 set]的更多相关文章

  1. bzoj 3991: [SDOI2015]寻宝游戏 虚树 set

    目录 题目链接 题解 代码 题目链接 bzoj 3991: [SDOI2015]寻宝游戏 题解 发现每次答案就是把虚树上的路径*2 接在同一关键点上的点的dfs序是相邻的 那么用set动态维护dfs序 ...

  2. BZOJ 3991: [SDOI2015]寻宝游戏 树链的并+set

    Description 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可 ...

  3. bzoj 3991: [SDOI2015]寻宝游戏

    Description 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可 ...

  4. 树形结构的维护:BZOJ 3991: [SDOI2015]寻宝游戏

    Description 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可 ...

  5. [BZOJ 3991][SDOI2015]寻宝游戏(dfs序)

    题面 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路 ...

  6. 【BZOJ】3991: [SDOI2015]寻宝游戏 虚树+DFS序+set

    [题意]给定n个点的带边权树,对于树上存在的若干特殊点,要求任选一个点开始将所有特殊点走遍后返回.现在初始没有特殊点,m次操作每次增加或减少一个特殊点,求每次操作后的总代价.n,m<=10^5. ...

  7. BZOJ.3991.[SDOI2015]寻宝游戏(思路 set)

    题目链接 从哪个点出发最短路径都是一样的(最后都要回来). 脑补一下,最短路应该是按照DFS的顺序,依次访问.回溯遍历所有点,然后再回到起点. 即按DFS序排序后,Ans=dis(p1,p2)+dis ...

  8. 3991: [SDOI2015]寻宝游戏

    3991: [SDOI2015]寻宝游戏 https://www.lydsy.com/JudgeOnline/problem.php?id=3991 分析: 虚树+set. 要求树上许多点之间的路径的 ...

  9. 【BZOJ】3991: [SDOI2015]寻宝游戏

    题意 给一个\(n\)个点带边权的树.有\(m\)次操作,每一次操作一个点\(x\),如果\(x\)已经出现,则\(x\)消失.否则\(x\)出现.每一操作后,询问从某个点开始走,直到经过所有出现的点 ...

随机推荐

  1. Redis进阶实践之四Redis的基本数据类型

    一.引言    今天正式开始了Redis的学习,如果要想学好Redis,必须先学好Redis的数据类型.Redis为什么会比以前的Memchaed等内存缓存软件使用的更频繁,适用范围更广呢?就是因为R ...

  2. android文件管理器源码、斗鱼直播源码、企业级erp源码等

    Android精选源码 文件清理管理器 自定义水平带数字的进度条以及自定义圆形带数字的进度条 利用sectionedRecyclerViewAdapter实现分组列表的recyclerView源码 流 ...

  3. EMC题

    [面试题]EMC易安信面试题解 1. 除以59的余数是多少. 来自wiki:费马小定理是数论中的一个定理:假如a是一个整数,p是一个質数,那么 如果a不是p的倍数,这个定理也可以写成 这个书写方式更加 ...

  4. Python scrapy------分类获取美团整站数据

    欢迎联系讨论:qq:1170370113 以下是我们获取美团页面的城市信息 获取到了城市信息以后我们可以进行分类保存,以便于后续能够分类获取数据 获取我们需要城市的景区的所有相关id并且进行保存 最后 ...

  5. php页面zend加密乱码的解决办法

    http://www.chinaz.com/program/2008/1021/41485.shtml?qq-pf-to=pcqq.group 今天在服务器部署一个php程序是有zend加密的页面出现 ...

  6. SSL和SSH有什么区别

    SSL 是一种安全协议,它为网络(例如因特网)的通信提供私密性.SSL 使应用程序在通信时不用担心被窃听和篡改. SSL 实际上 是共同工作的两个协议:"SSL 记录协议"(SSL ...

  7. OpenCV鼠标滑轮事件

    鼠标的滑轮事件实现图像的缩放很方便,具体在回调函数中如下写: 其中scale可以在外部定义为全局变量,通过响应CV_EVENT_MOUSEWHEEL滑轮事件获取Scale的具体值. 获取Scale值需 ...

  8. Hive 多分隔符的使用 (转载)

    方法一)通过org.apache.hadoop.hive.contrib.serde2.RegexSerDe格式的serde. 1) 建表语句 #指定以^|~作为分隔符 CREATE TABlE ta ...

  9. CPU频率

    CPU频率 CPU频率,就是CPU的时钟频率,简单说是CPU运算时的工作的频率(1秒内发生的同步脉冲数)的简称. 概念 CPU频率,就是CPU的时钟频率,简单说是CPU运算时的工作的频率(1秒内发生的 ...

  10. 关于Serializable的serialVersionUID

    在实现了Serializable接口的class中,需要声明一个long serialVersionUID,用来标明当前class的版本号,但很多人在编程时,总是不原意去声明这个serialVersi ...