洛谷传送门
BZOJ传送门
题目描述
小B最近正在玩一个寻宝游戏,这个游戏的地图中有 N N 个村庄和条道路,并且任何两个村庄之间有且仅有一条路径可达。游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路上行走,若走到某个村庄中有宝物,则视为找到该村庄内的宝物,直到找到所有宝物并返回到最初转移到的村庄为止。
小B希望评测一下这个游戏的难度,因此他需要知道玩家找到所有宝物需要行走的最短路程。但是这个游戏中宝物经常变化,有时某个村庄中会突然出现宝物,有时某个村庄内的宝物会突然消失,因此小B需要不断地更新数据,但是小B太懒了,不愿意自己计算,因此他向你求助。为了简化问题,我们认为最开始时所有村庄内均没有宝物
输入输出格式
输入格式:
第一行,两个整数 N N 、,其中 M M 为宝物的变动次数。接下来的行,每行三个整数 x x 、、 z z ,表示村庄、 y y 之间有一条长度为的道路。接下来的 M M 行,每行一个整数,表示一个宝物变动的操作。若该操作前村庄 t t 内没有宝物,则操作后村庄内有宝物;若该操作前村庄t内有宝物,则操作后村庄内没有宝物。
输出格式:
行,每行一个整数,其中第 i i 行的整数表示第次操作之后玩家找到所有宝物需要行走的最短路程。若只有一个村庄内有宝物,或者所有村庄内都没有宝物,则输出 0 0 。
输入输出样例
输入样例#1:
4 5
1 2 30
2 3 50
2 4 60
2
3
4
2
1
输出样例#1:
0
100
220
220
280
说明
1≤M≤100000 1 ≤ M ≤ 100000
对于全部的数据, 1≤z≤109 1 ≤ z ≤ 10 9
解题分析
事实上, 从哪个点开始并不重要, 我们只要保证同一子树内的有宝物的点一起访问即可, 这就是 DFS D F S 序的顺序。
那么我们用 set s e t 维护有宝物的点的 DFS D F S 序, 插入、 删除时查找前驱后继, 更新它们之间的贡献。 预处理树剖 LCA L C A 和离 1 1 <script type="math/tex" id="MathJax-Element-82">1</script>号点的距离, 这样方便算两点之间距离。
代码如下:
#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <set>
#define R register
#define IN inline
#define W while
#define MX 100500
#define gc getchar()
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc);
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
}
bool vis[MX];
int dot, q, arr, cnt;
int dfn[MX], head[MX], dep[MX], son[MX], siz[MX], fat[MX], topf[MX], rec[MX];
long long dis[MX], ans;
struct Edge {int to, len, nex;} edge[MX << 1];
std::set <int> st;
IN void add(R int from, R int to, R int len)
{edge[++cnt] = {to, len, head[from]}, head[from] = cnt;}
void DFS(R int now)
{
siz[now] = 1; dfn[now] = ++arr; rec[arr] = now;
for (R int i = head[now]; i; i = edge[i].nex)
{
if(edge[i].to == fat[now]) continue;
dis[edge[i].to] = dis[now] + edge[i].len;
dep[edge[i].to] = dep[now] + 1;
fat[edge[i].to] = now;
DFS(edge[i].to);
siz[now] += siz[edge[i].to];
if(siz[edge[i].to] > siz[son[now]]) son[now] = edge[i].to;
}
}
void DFS(R int now, R int grand)
{
topf[now] = grand;
if(!son[now]) return;
DFS(son[now], grand);
for (R int i = head[now]; i; i = edge[i].nex)
{
if(edge[i].to == fat[now] || edge[i].to == son[now]) continue;
DFS(edge[i].to, edge[i].to);
}
}
IN int query(R int x, R int y)
{
W (topf[x] ^ topf[y])
{
if(dep[topf[x]] < dep[topf[y]]) std::swap(x, y);
x = fat[topf[x]];
}
return dep[x] < dep[y] ? x : y;
}
IN long long getdis(R int x, R int y)
{
int lca = query(x, y);
return dis[x] + dis[y] - (dis[lca] << 1);
}
std::set <int> :: iterator it, pre, nxt;
int main(void)
{
int a, b, c;
in(dot), in(q);
for (R int i = 1; i < dot; ++i)
in(a), in(b), in(c), add(a, b, c), add(b, a, c);
DFS(1), DFS(1, 1); st.insert(0), st.insert(dot + 1);
W (q--)
{
in(a); b = c = 0;
if(vis[a])
{
nxt = pre = it = st.find(dfn[a]); nxt++, pre--; b = *pre, c = *nxt;
if(b > 0) ans -= getdis(rec[b], a);
if(c <= dot) ans -= getdis(rec[c], a);
if(b > 0 && c <= dot) ans += getdis(rec[b], rec[c]);
st.erase(it);
}
else
{
st.insert(dfn[a]);
nxt = pre = it = st.find(dfn[a]); nxt++, pre--; b = *pre, c = *nxt;
if(b > 0) ans += getdis(rec[b], a);
if(c <= dot) ans += getdis(rec[c], a);
if(b > 0 && c <= dot) ans -= getdis(rec[b], rec[c]);
}
vis[a] ^= 1; b = *++st.find(0), c = *--st.find(dot + 1);
printf("%lld\n", ans + (st.size() > 2 ? getdis(rec[b], rec[c]) : 0));//因为要回到原点, 我们需要加上DFS序相差最大的两个点之间的距离
}
}