bzoj 3991 寻宝游戏
题目大意:
一颗树 有一个点的集合
对于每个集合的答案为 从集合内一个点遍历集合内所有点再返回的距离最小值
每次可以选择一个点 若在集合外便加入集合 若在集合内就删除
求每次操作后这个集合的答案
思路:
对于每个集合
它的答案一定为从dfs序最小的开始依次遍历再回来
当加入一个点x的时候 可以找到它dfs序的前驱与后继 画图可得 ans+=dis(pre,x)+dis(x,sub)-dis(pre,sub) 删除的时候为ans-=
特别地 当x没有前驱或后继时 前驱为最大值 后继为最小值(当做一个环
因此我们维护一颗平衡树搞一下即可
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
#define inf 2139062143
#define ll long long
#define MAXN 100100
using namespace std;
inline int read()
{
int x=,f=;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-;ch=getchar();}
while(isdigit(ch)) {x=x*+ch-'';ch=getchar();}
return x*f;
}
int ch[MAXN][],fa[MAXN],sz,cnt[MAXN],val[MAXN],size[MAXN],rt;
int which(int x) {return x==ch[fa[x]][];}
int find_pre()
{
int pos=ch[rt][];
while(ch[pos][]) pos=ch[pos][];
return pos;
}
int find_max()
{
int pos=ch[rt][];
while(ch[pos][]) pos=ch[pos][];
return pos;
}
int find_min()
{
int pos=ch[rt][];
while(ch[pos][]) pos=ch[pos][];
return pos;
}
int find_sub()
{
int pos=ch[rt][];
while(ch[pos][]) pos=ch[pos][];
return pos;
}
void upd(int x)
{
if(!x) return ;
size[x]=cnt[x]+size[ch[x][]]+size[ch[x][]];
}
void rotate(int pos)
{
int f=fa[pos],ff=fa[f],k=which(pos);
ch[f][k]=ch[pos][k^],fa[ch[f][k]]=f,fa[f]=pos,ch[pos][k^]=f,fa[pos]=ff;
if(ff) ch[ff][ch[ff][]==f]=pos;
upd(f);upd(pos);
}
void splay(int x)
{
for(int f;f=fa[x];rotate(x))
if(fa[f]) rotate((which(x)==which(f)?f:x));
rt=x;
}
void Insert(int x)
{
int pos=rt,f=;
while()
{
if(val[pos]==x) {cnt[pos]++,upd(pos);upd(f);splay(pos);return ;}
f=pos,pos=ch[pos][x>val[pos]];
if(!pos)
{
ch[++sz][]=ch[sz][]=,fa[sz]=f,val[sz]=x,cnt[sz]=size[sz]=,ch[f][x>val[f]]=sz;
upd(f);splay(sz);return ;
}
}
}
void insert(int x)
{
if(!rt) {val[++sz]=x,ch[sz][]=ch[sz][]=fa[sz]=,cnt[sz]=size[sz]=,rt=sz;return;}
Insert(x);
}
int find_rank(int x)
{
int res=,pos=rt;
while()
{
if(x<val[pos]) pos=ch[pos][];
else
{
res+=size[ch[pos][]];
if(val[pos]==x) {splay(pos);return res+;}
res+=cnt[pos],pos=ch[pos][];
}
}
}
void dlt(int x)
{
if(cnt[rt]>) {cnt[rt]--;return ;}
if(!ch[rt][]&&!ch[rt][]) {rt=;return ;}
if(!ch[rt][]||!ch[rt][])
{
int k=!ch[rt][]?:;
rt=ch[rt][k],fa[rt]=;
return ;
}
int k=find_pre(),tmp=rt;
splay(k);fa[ch[tmp][]]=rt;
ch[rt][]=ch[tmp][],rt=k;
}
int n,m,nxt[MAXN<<],fst[MAXN],to[MAXN<<],Val[MAXN<<],Cnt;
int f[MAXN][],dep[MAXN],s[MAXN],k[MAXN],tot,hsh[MAXN],HSH[MAXN],vis[MAXN];
ll ans,dis[MAXN];
void add(int u,int v,int w) {nxt[++Cnt]=fst[u],fst[u]=Cnt,to[Cnt]=v,Val[Cnt]=w;}
void dfs(int x)
{
for(int i=;(<<i)<=dep[x];i++) f[x][i]=f[f[x][i-]][i-];
hsh[x]=++tot,HSH[tot]=x;
for(int i=fst[x];i;i=nxt[i])
if(to[i]!=f[x][])
{
dis[to[i]]=dis[x]+Val[i],dep[to[i]]=dep[x]+;
f[to[i]][]=x;dfs(to[i]);
}
}
int lca(int u,int v)
{
int t;
if(dep[u]<dep[v]) swap(u,v);
t=dep[u]-dep[v];
for(int i=;i<;i++)
if((<<i)&t) u=f[u][i];
if(u==v) return u;
for(int i=;i>=;i--)
if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
return f[u][];
}
inline ll calc(int u,int v) {return dis[HSH[u]]+dis[HSH[v]]-(dis[lca(HSH[u],HSH[v])]<<);}
int main()
{
n=read(),m=read();int a,b,c;
for(int i=;i<n;i++) {a=read(),b=read(),c=read();add(a,b,c);add(b,a,c);}
dfs();
while(m--)
{
c=hsh[read()],vis[c]^=;
if(vis[c])
{
if(!rt) {puts("");insert(c);continue;}insert(c);
a=val[find_pre()],b=val[find_sub()];
if(!a) a=val[find_max()];if(!b) b=val[find_min()];
if(!a) a=val[rt];if(!b) b=val[rt];
ans+=calc(a,c)+calc(b,c)-calc(a,b);
}
else
{
a=find_rank(c);
a=val[find_pre()],b=val[find_sub()];
if(!a) a=val[find_max()];if(!b) b=val[find_min()];
if(!a) a=val[rt];if(!b) b=val[rt];
ans-=calc(a,c)+calc(b,c)-calc(a,b);
dlt(c);
}
printf("%lld\n",ans);
}
}
bzoj 3991 寻宝游戏的更多相关文章
- bzoj 3991: [SDOI2015]寻宝游戏 虚树 set
目录 题目链接 题解 代码 题目链接 bzoj 3991: [SDOI2015]寻宝游戏 题解 发现每次答案就是把虚树上的路径*2 接在同一关键点上的点的dfs序是相邻的 那么用set动态维护dfs序 ...
- 【BZOJ】【3991】【SDOI2015】寻宝游戏
dfs序 我哭啊……这题在考试的时候(我不是山东的,CH大法吼)没想出来……只写了50分的暴力QAQ 而且苦逼的写的比正解还长……我骗点分容易吗QAQ 骗分做法: 1.$n,m\leq 1000$: ...
- 树形结构的维护:BZOJ 3991: [SDOI2015]寻宝游戏
Description 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可 ...
- bzoj 3991: [SDOI2015]寻宝游戏
Description 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可 ...
- 寻宝游戏(bzoj 3991)
Description 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可 ...
- BZOJ 3991: [SDOI2015]寻宝游戏 树链的并+set
Description 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可 ...
- [BZOJ 3991][SDOI2015]寻宝游戏(dfs序)
题面 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路 ...
- BZOJ.5285.[AHOI/HNOI2018]寻宝游戏(思路 按位计算 基数排序..)
BZOJ LOJ 洛谷 话说vae去年的专辑就叫寻宝游戏诶 只有我去搜Mystery Hunt和infinite corridor了吗... 同样按位考虑,假设\(m=1\). 我们要在一堆\(01\ ...
- 3991: [SDOI2015]寻宝游戏
3991: [SDOI2015]寻宝游戏 https://www.lydsy.com/JudgeOnline/problem.php?id=3991 分析: 虚树+set. 要求树上许多点之间的路径的 ...
随机推荐
- Leetcode 284.顶端迭代器
顶端迭代器 给定一个迭代器类的接口,接口包含两个方法: next() 和 hasNext().设计并实现一个支持 peek() 操作的顶端迭代器 -- 其本质就是把原本应由 next() 方法返回的元 ...
- POJ2421 Constructing Roads
Constructing Roads 这道题很水,就是一个裸的最小生成树,最不过把已经连接的节点的值再设为0. 代码: #include<cstdio> #include<cstri ...
- 洛谷P1276 校门外的树(增强版)未完工
题目描述 校门外马路上本来从编号0到L,每一编号的位置都有1棵树.有砍树者每次从编号A到B处连续砍掉每1棵树,就连树苗也不放过(记 0 A B ,含A和B):幸运的是还有植树者每次从编号C到D 中凡是 ...
- 最大数(cogs 1844)
[题目描述] 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作.语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值.限制:L不超过当前数列的长度. 2. 插入操作 ...
- vim—基本命令1
---------------------------------------------------------------2015.07.27 :b 1 -> 切换到当前缓冲区 :2 4 ...
- C语言基本概念之表达式
原文地址:http://blog.csdn.net/astrotycoon/article/details/50857326 [侵删] 什么是表达式(表达式的定义)? 对于表达式的定义,好像从来没有人 ...
- oc温习三:常用函数
参考文章: 算术函数 [算术函数] 函数名 说明 int rand() 随机数生成.(例)srand(time(nil)); //随机数初期化int val = rand()P; //0-49之间的随 ...
- HUNAN 11567 Escaping (最大流)
http://acm.hunnu.edu.cn/online/?action=problem&type=list&courseid=0&querytext=&pagen ...
- 洛谷—— P2812 校园网络
P2812 校园网络 题目背景 浙江省的几所OI强校的神犇发明了一种人工智能,可以AC任何题目,所以他们决定建立一个网络来共享这个软件.但是由于他们脑力劳动过多导致全身无力身体被♂掏♂空,他们来找你帮 ...
- 重装JDK后Tomcat和Eclipse的配置
比如JDK之前是1.8.0_31的,升级之后变成了1.8.0_131之后,Tomcat需要做如下配置: 对于Eclipse中之前配置的Tomcat需要删除后重新添加一个.