洛谷 P2633 Count on a tree

我与影子孤独终老i 提交于 2020-08-08 19:52:47

思路

看到路径上\(k\)小值,首先想到主席树

不会主席树的建议来这里看一下【AgOHの数据结构】主席树(友情提示:此链接为B站视频

但是这是棵树,并不是序列,我们应该怎么办呢?

显然,我们可以像序列前缀和一样,建立树上前缀和:以点的\(dfs\)序为下标,以点权为区间建立主席树。

那么查询时\((x,y)\)这条链间的点数就是

\[sum[x]+sum[y]-sum[lca]-sum[fa[lca]] \]

这里用到了一点点树上差分的思想,自己思考一下

至于\(LCA\),用树剖或者倍增求一下就好了

然后查询的时候就可以像序列上一样了

代码

/*
Author:Loceaner
*/
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int A = 5e5 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

inline int read() {
  char c = getchar(); int x = 0, f = 1;
  for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
  for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
  return x * f;
}

struct node { int to, nxt; } e[A];
int n, m, head[A], cnt/*边数*/, tcnt/*主席树数*/, tot/*离散化之后*/;
int a[A], b[A], lastans; 

inline void add(int from, int to) {
  e[++cnt].to = to;
  e[cnt].nxt = head[from];
  head[from] = cnt;
}

/*主席树*/
int root[A];
struct tree { int l, r, sum; } t[A * 10];

inline int getid(int x) {
  return lower_bound(b + 1, b + 1 + tot, x) - b;
}

void insert(int &now, int pre, int l, int r, int pos) {
  t[++cnt] = t[pre], t[now = cnt].sum++;
  if (l == r) return;
  int mid = (l + r) >> 1;
  if (pos <= mid) insert(t[now].l, t[pre].l, l, mid, pos);
  else insert(t[now].r, t[pre].r, mid + 1, r, pos);
}

int query(int x, int y, int lca, int fa_lca, int l, int r, int k) {
  if (l == r) return l;
  int mid = (l + r) >> 1;
  int tmp = t[t[x].l].sum + t[t[y].l].sum - t[t[lca].l].sum - t[t[fa_lca].l].sum;
  if (k <= tmp) query(t[x].l, t[y].l, t[lca].l, t[fa_lca].l, l, mid, k);
  //debug:k<=tmp写成k>=tmp 
  else query(t[x].r, t[y].r, t[lca].r, t[fa_lca].r, mid + 1, r, k - tmp);
}

/*树剖*/
int dep[A], fa[A], top[A], siz[A], son[A];

void prepare(int x, int f) {
  dep[x] = dep[f] + 1, fa[x] = f, siz[x] = 1;
  insert(root[x], root[fa[x]], 1, n, getid(a[x]));
  for (int i = head[x]; i; i = e[i].nxt) {
    int to = e[i].to;
    if (to == f) continue;
    prepare(to, x), siz[x] += siz[to];
    //debug:prepare写成dfs 
    if (siz[to] > siz[son[x]]) son[x] = to;
  }
}

void dfs(int x, int tp) {
  top[x] = tp;
  if (son[x]) dfs(son[x], tp);
  for (int i = head[x]; i; i = e[i].nxt) {
    int to = e[i].to;
    if (to == fa[x] || to == son[x]) continue;
    dfs(to, to);
  }
}

int LCA(int x, int y) {
  while (top[x] != top[y]) {
    if (dep[top[x]] < dep[top[y]]) swap(x, y);
    x = fa[top[x]];
  }
  if (dep[x] > dep[y]) swap(x, y);
  return x;
}

int main() {
  n = read(), m = read();
  for (int i = 1; i <= n; i++) b[i] = a[i] = read();
  for (int i = 1; i < n; i++) {
    int x = read(), y = read();
    add(x, y), add(y, x);
  } 
  sort(b + 1, b + 1 + n);
  tot = unique(b + 1, b + 1 + n) - b - 1;
  prepare(1, 0), dfs(1, 1);
  while (m--) {
    int x = read(), y = read(), k = read();
    x ^= lastans;
    int lca = LCA(x, y);
    lastans = b[query(root[x], root[y], root[lca], root[fa[lca]], 1, n, k)];
    cout << lastans << '\n';
  }
  return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!