BZOJ 3991 寻宝游戏

题意: https://www.lydsy.com/JudgeOnline/problem.php?id=3991

sol:

休闲题。

实际上虚树是不能做的。

发现最短路线其实就是按照dfn序首位相连这么走。

考虑插入一个 d f n = x dfn = x dfn=x的点,找前驱后继(注意是环状)然后拿 l c a lca lca什么的随便维护下距离就好。

好久没写倍增 l c a lca lca了,写个练练手。


#include
#include
#include
#include
const int N = 1e5+7;
typedef long long LL;
const int upmax = 2e5;
struct Edge {
  int to, next, w;
}e[N*2];
int cnt, tot, pcnt, last[N], st[N*2][19];
inline int add(int u, int v, int w) {
  e[++cnt].next = last[u], e[cnt].w = w, e[cnt].to = v, last[u] = cnt;
}
int dep[N], dfn[N], idf[N]; LL dis[N];
#define R register
void dfs(int x, int fa, int depth) {
  dep[x] = depth, dfn[x] = ++tot, st[x][0] = fa, idf[tot] = x;
  for (int o = last[x]; o; o = e[o].next) {
    int to = e[o].to;
    if (to == fa) continue;
    dis[to] = dis[x] + (LL)(e[o].w);
    dfs(to, x, depth + 1);
  }
}
int qlog[N*2], logw;
inline int LCA(int x, int y) {
  if (dep[y] < dep[x]) std :: swap(x, y);
  int up = logw + 1;
  for (int i = up; i >= 0; i--)
    if (dep[st[y][i]] >= dep[x]) y = st[y][i];
  if (x == y) return x;
  for (int i = up; i >= 0; i--)
    if (st[y][i] != st[x][i]) y = st[y][i], x = st[x][i];
  return st[x][0];
}
inline LL dist(int x, int y) {
  return dis[x] + dis[y] - 2LL * dis[LCA(x, y)];
}
int n, m, vis[N];
void init() {
  scanf("%d%d", &n, &m);
  for (int i = 1; i < n; i++) {
    int x, y, z;
    scanf("%d%d%d", &x, &y, &z);
    add(x, y, z), add(y, x, z);
  }
  dfs(1, 1, 1);
  for (int i = 2; i <= upmax; i++) qlog[i] = qlog[i >> 1] + 1;
  logw = qlog[n];
  for (R int i = 1; i <= logw; i++)
    for (R int j = 1; j <= n; j++) 
      st[j][i] = st[st[j][i - 1]][i - 1];
}
std :: set<int> s;
std :: set<int> :: iterator it, x;
LL ans = 0;
void solve() {
  for (int i = 1; i <= m; i++) {
    int x, y, z;
    scanf("%d", &x), x = dfn[x];
    if (!vis[idf[x]]) s.insert(x);
    y = idf[(it = s.lower_bound(x)) == s.begin() ? *--s.end() : *--it];
    z = idf[(it = s.upper_bound(x)) == s.end() ? *s.begin() : *it];
    if (vis[idf[x]]) s.erase(x);
    x = idf[x];
    LL d = dist(x, y) + dist(x, z) - dist(y, z);
    if (!vis[x]) vis[x] = 1, ans += d;
    else vis[x] = 0, ans -= d;
    printf("%lld\n", ans); 
  } 
}
int main() {
  init(), solve();
}

你可能感兴趣的:(树上差分)