bzoj3991 LCA + set
https://www.lydsy.com/JudgeOnline/problem.php?id=3991
小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达。游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路上行走,若走到某个村庄中有宝物,则视为找到该村庄内的宝物,直到找到所有宝物并返回到最初转移到的村庄为止。小B希望评测一下这个游戏的难度,因此他需要知道玩家找到所有宝物需要行走的最短路程。但是这个游戏中宝物经常变化,有时某个村庄中会突然出现宝物,有时某个村庄内的宝物会突然消失,因此小B需要不断地更新数据,但是小B太懒了,不愿意自己计算,因此他向你求助。为了简化问题,我们认为最开始时所有村庄内均没有宝物的
很显然问题的关键在于处理每一次变更的宝藏点的信息,一个容易发现的结论是,只要是从宝藏点出发,无论哪个点出发都能寻找到一条最优的路径,因为行走的路径会是一个环,对于K个宝藏点,行走的路径是1->2,2->3,3->4.........k-1 -> k,k ->1,所以说,对于一条链,每一次变更只要找到这个点插入的前驱pre和后继nxt,插入的时候删除dis(pre,nxt),加上dis(pre,x) + dis(x,pre)就可以了,删除同理。
问题在于怎么去寻找他的前驱和后继,对于一棵树来说,想要遍历所有的关键点,贪心的想到是和dfs一样走,可以最短的经过一圈所有的关键点,所以我们掏出一手dfs序的前序,对于每一个数字的更改,去寻找他在dfs序里面前后最接近的宝藏点就是他的前驱和后继。
#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x);
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
const double eps = 1e-;
const int maxn = 1e5 + ;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + ;
const int SP = ;
int N,M,K;
struct Edge{
int to,next;
LL dis;
}edge[maxn * ];
int dfn[maxn];
int head[maxn],tot;
int id;
int idx[maxn];
LL Dis[maxn];
int pa[maxn][SP],dep[maxn];
int vis[maxn];
set<int>Q;
void init(){
Mem(head,-);
tot = ;
}
void add(int u,int v,LL w){
edge[tot].to = v;
edge[tot].next = head[u];
edge[tot].dis = w;
head[u] = tot++;
}
void dfs(int u,int la){
dfn[u] = ++id; idx[id] = u;
pa[u][] = la;
For(i,,SP - ) pa[u][i] = pa[pa[u][i - ]][i - ];
for(int i = head[u]; ~i; i = edge[i].next){
int v = edge[i].to;
if(v == la) continue;
dep[v] = dep[u] + ;
Dis[v] = Dis[u] + edge[i].dis;
dfs(v,u);
}
}
int lca(int u,int v){
if(dep[u] < dep[v]) swap(u,v);
int t = dep[u] - dep[v];
For(i,,SP - ) if(t & ( << i)) u = pa[u][i];
_For(i,SP - ,){
int uu = pa[u][i],vv = pa[v][i];
if(uu != vv){
u = uu;
v = vv;
}
}
return u == v?u:pa[u][];
}
LL DIS(int u,int v){
return Dis[u] + Dis[v] - * Dis[lca(u,v)];
}
int main()
{
Sca2(N,M); init();
For(i,,N - ){
int u,v; Sca2(u,v);
LL w; Scl(w);
add(u,v,w); add(v,u,w);
}
Dis[] = ;id = ;dfs(,);
LL ans = ;
Q.insert(); Q.insert(N + );
while(M--){
int x; Sca(x);
if(vis[x]){
int pre = *--Q.find(dfn[x]),pro = *++Q.find(dfn[x]);
if(pre >= ) ans -= DIS(idx[pre],x);
if(pro <= N) ans -= DIS(idx[pro],x);
if(pre >= && pro <= N) ans += DIS(idx[pre],idx[pro]);
Q.erase(dfn[x]);
}else{
Q.insert(dfn[x]);
int pre = *--Q.find(dfn[x]),pro = *++Q.find(dfn[x]);
if(pre >= ) ans += DIS(idx[pre],x);
if(pro <= N) ans += DIS(idx[pro],x);
if(pre >= && pro <= N) ans -= DIS(idx[pre],idx[pro]);
}
LL z = ;
int s = *++Q.find(),e = *--Q.find(N + );
if(s >= && e <= N) z = DIS(idx[s],idx[e]);
Prl(ans + z);
vis[x] ^= ;
}
#ifdef VSCode
system("pause");
#endif
return ;
}
bzoj3991 LCA + set的更多相关文章
- bzoj3991 lca+dfs序应用+set综合应用
/* 给定一棵树,树上会出现宝物,也会有宝物消失 规定如果要收集树上所有宝物,就要选择一个点开始,到每个宝物点都跑一次,然后再回到那个点 现在给定m次修改,每次修改后树上就有一个宝物消失,或者一个宝物 ...
- bzoj3991: [SDOI2015]寻宝游戏--DFS序+LCA+set动态维护
之前貌似在hdu还是poj上写过这道题. #include<stdio.h> #include<string.h> #include<algorithm> #inc ...
- BZOJ3991:寻宝游戏 (LCA+dfs序+树链求并+set)
小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路上行走 ...
- BZOJ3991 [SDOI2015]寻宝游戏 【dfs序 + lca + STL】
题目 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路 ...
- BZOJ3991 寻宝游戏 LCA 虚树 SET
5.26 T1:寻宝游戏 Description 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄, ...
- 【bzoj3991】 寻宝游戏
http://www.lydsy.com/JudgeOnline/problem.php?id=3991 (题目链接) 题意 给出一个n个节点的带权树,m次操作每次修改一个关键点,求每次操作后,从其中 ...
- [BZOJ3991][SDOI2015]寻宝游戏
[BZOJ3991][SDOI2015]寻宝游戏 试题描述 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择 ...
- 【BZOJ3991】寻宝游戏(动态规划)
[BZOJ3991]寻宝游戏(动态规划) 题面 BZOJ 题解 很明显,从任意一个有宝藏的点开始,每次走到相邻的\(dfs\)的节点就行了. 证明? 类似把一棵树上的关键点全部标记出来 显然是要走一个 ...
- 【BZOJ3991】[SDOI2015]寻宝游戏 树链的并+set
[BZOJ3991][SDOI2015]寻宝游戏 Description 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩 ...
随机推荐
- BZOJ2219数论之神——BSGS+中国剩余定理+原根与指标+欧拉定理+exgcd
题目描述 在ACM_DIY群中,有一位叫做“傻崽”的同学由于在数论方面造诣很高,被称为数轮之神!对于任何数论问题,他都能瞬间秒杀!一天他在群里面问了一个神题: 对于给定的3个非负整数 A,B,K 求出 ...
- Codeforces Round #429 Div. 1
A:甚至连题面都不用仔细看,看一下样例就知道是要把大的和小的配对了. #include<iostream> #include<cstdio> #include<cmath ...
- 微信小程序——安装开发工具和环境【二】
准备 开发工具下载 获取APPID 安装工具 安装 接受协议 选择安装位置 等待安装完成 安装完成 选择项目 选择小程序 填写信息 确定 无误后,点击确定进入开发页面 建立普通快速启动模板界面
- HDU5773-The All-purpose Zero-多校#41010-最长上升子序列问题
只想到了朴素的n^2做法,然后发现可以用splay维护.于是调了几个小时的splay... splay的元素是从第二个开始的!第一个是之前插入的头节点! #include <cstdio> ...
- 洛谷P1007独木桥题解
题目 这个题看起来很难,但是只要理解了题目,一切都显得简单. 你只需想象出来每个人在碰撞之后仅仅是交换了灵魂, 这样一来,代码就好写了. 附代码: #include<iostream> # ...
- prufer序列
介绍 其实是\(pr\ddot{u}fer\)序列 什么是prufer序列? 我们认为度数为\(1\)的点是叶子节点 有一颗无根树,每次选出编号最小的叶子节点,加到当前prufer序列的后面,然后删掉 ...
- CODEFORCES掉RATING记 #4
比赛:Codeforces Round #427 (Div. 2) 时间:2017.7.31晚 开场发现有6道题,都是水题(可能我只会做水题) A:比较\(2t_1+sv_1\)与\(2t_2+sv_ ...
- Codeforces Round #545 (Div. 2) D
链接:http://codeforces.com/contest/1138/problem/D 啊啊啊啊啊啊,自闭啊,比赛的时候判断条件 if(s1[i-1]=='0') aa++;写成了 if(s1 ...
- springmvc返回xml格式、json格式数据
问:@ResponseBody注解怎么指定返回xml 还是json答:@RequestMapping 的produces 属性指定 produces = "application/xml&q ...
- Fiddler中显示IP方法
如何在fiddler查看到请求的IP地址?就像下面这样 打开fiddler, 快捷键Ctrl+R 或者 菜单->Rules->Customize Rules…,然后在CustomRul ...