P4197 Peaks

知识点: 线段树合并,主席树,Kruscal 重构树

原题面 Luogu


题意简述

给定一张 \(n\) 个点,\(m\) 条边的图,点有点权,边有边权。
每次询问给定参数 \(v, x, k\),求从节点 \(v\) 出发,只经过边权 \(\le x\) 的边,能到达的所有节点中,第 \(k\) 大的点权。
\(1\le n\le 10^5\),\(0\le m, q\le 5\times 10^5\),点权边权 \(\le 10^9\)。


分析题意

在线

这是一个显然的在线做法。
只能经过边权 \(\le x\) 的边,套路地考虑 Kruscal 重构树。
边权 \(\rightarrow\) 点权,重构树上的点权值 自根向叶子递减。

对于每一个询问,从 \(v\) 倍增到点权值 \(\le x\) 的,点权最大的祖先 \(u\)。
显然,\(u\) 的子树中所有节点的点权 \(\le x\)。
从 \(v\) 出发,可到达 \(u\) 子树中所有节点。

答案即该子树中所有 叶节点,即原图中的节点的第 \(k\) 大点权。
子树查询问题,考虑处理出 \(dfs\) 序,子树中的点被转化为序列上的一段连续区间。
静态区间第 \(k\) 大值问题,主席树查询即可。

复杂度 \(O(m\log m + q\log n)\)。


离线

这是一个显然的离线做法。
离线询问,将询问按边权限制 \(x\) 升序排序。
枚举询问时考虑边权比上个询问限制 更大的边的影响。

问题变为每次向图中添加一些边,并查询给定节点所在连通块点权的 第 \(k\) 大值。
变成了这题:bzoj4399. 魔法少女LJJ,线段树合并 + 并查集简单维护即可。


代码实现

//知识点:权值线段树,线段树合并,并查集 
/*
By:Luckyblock
*/
#include <cstdio>
#include <ctype.h>
#include <cstring>
#include <algorithm>
#define ll long long
#define ls (t[now].lson)
#define rs (t[now].rson)
const int kMaxn = 5e5 + 10;
const int kInf = 1e9 + 5;
//=============================================================
struct Edge {
  int u, v, c;
} e[kMaxn];
struct Query {
  int v, x, k, id;
} q[kMaxn];
struct SegmentTree {
  int lson, rson, sum;
} t[kMaxn << 5];
int n, m, qnum, fa[kMaxn];
int node_num, root[kMaxn], ans[kMaxn];
//=============================================================
inline int read() {
  int f = 1, w = 0; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
  return f * w;
}
void GetMax(int &fir, int sec) {
  if (sec > fir) fir = sec;
}
void GetMin(int &fir, int sec) {
  if (sec < fir) fir = sec;
}
bool CompareEdge(Edge fir, Edge sec) {
  return fir.c < sec.c;
}
bool CompareQuery(Query fir, Query sec) {
  return fir.x < sec.x;
}
int Find(int x) {
  return fa[x] == x ? x : fa[x] = Find(fa[x]);
}
void Update(int now) {
  t[now].sum = t[ls].sum + t[rs].sum;
}
void Insert(int &now, int L, int R, int pos, int val) {
  if (! now) now = ++ node_num;
  if (L == R) {
    t[now].sum += val;
    return ;
  }
  int mid = (L + R) >> 1;
  if (pos <= mid) Insert(ls, L, mid, pos, val);
  else Insert(rs, mid + 1, R, pos, val);
  Update(now);
}
int Merge(int now, int y, int L, int R) {
  if (! now || ! y) return now + y;
  if (L == R) {
    t[now].sum += t[y].sum;
    return now;
  }
  int mid = (L + R) >> 1;
  ls = Merge(ls, t[y].lson, L, mid);
  rs = Merge(rs, t[y].rson, mid + 1, R);
  Update(now);
  return now;
}
void Union(int x, int y) {
  int r1 = Find(x), r2 = Find(y);
  if (r1 == r2) return ;
  fa[r1] = r2;
  root[r2] = Merge(root[r2], root[r1], 1, kInf);
}
void Prepare() {
  n = read(), m = read(), qnum = read();
  for (int i = 1; i <= n; ++ i) Insert(root[i], 1, kInf, read(), 1);
  for (int i = 1; i <= m; ++ i) {
    int u = read(), v = read(), c = read();
    e[i] = (Edge) {u, v, c};
  }
  for (int i = 1; i <= qnum; ++ i) {
    int v = read(), x = read(), k = read();
    q[i] = (Query) {v, x, k, i};
  }
  for (int i = 1; i <= n; ++ i) fa[i] = i;
  std :: sort(e + 1, e + m + 1, CompareEdge);
  std :: sort(q + 1, q + qnum + 1, CompareQuery);
}
int Query(int now, int L, int R, int k) {
  if (L == R) return L;
  int size = t[rs].sum, mid = (L + R) >> 1;
  if (k <= size) return Query(rs, mid + 1, R, k);
  else return Query(ls, L, mid, k - size);
}
//=============================================================
int main() {
  Prepare();
  for (int i = 1, j = 0; i <= qnum; ++ i) {
    int v = q[i].v, x = q[i].x, k = q[i].k;
    for (; j <= m && e[j].c <= x; ++ j) Union(e[j].u, e[j].v);
    if (t[root[Find(v)]].sum < k) {
      ans[q[i].id] = - 1;
    } else {
      ans[q[i].id] = Query(root[Find(v)], 1, kInf, k);
    } 
  }
  for (int i = 1; i <= qnum; ++ i) printf("%d\n", ans[i]);
  return 0;
}

没写完的 Kruscal 重构树在线做法。
调不出来弃了!

//知识点:Kruscal重构树,主席树 
/* 
By:Luckyblock 
*/
#include <cstdio>
#include <ctype.h>
#include <cstring>
#include <algorithm>
#define ll long long
#define ls (t[now].lson)
#define rs (t[now].rson)
const int kMaxn = 5e2 + 10;
const int kInf = 1e9 + 2077;
//=============================================================
struct Edge {
  int u, v, w, ne;
} e[kMaxn << 3], e1[kMaxn];
struct SegmentTree {
  int lson, rson, size;
} t[kMaxn << 5];
int n, m, q, edge_num, node_num, h[kMaxn], head[kMaxn];
int Dfn, root, bel[kMaxn], hard[kMaxn], dep[kMaxn], dfn[kMaxn], size[kMaxn], fa[kMaxn][25];
int tree_node_num, data[kMaxn], val[kMaxn], Root[kMaxn];
int map[kMaxn];
//=============================================================
inline int read() {
  int f = 1, w = 0; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
  return f * w;
}
void GetMax(int &fir, int sec) {
  if (sec > fir) fir = sec;
}
void GetMin(int &fir, int sec) {
  if (sec < fir) fir = sec;
}
bool Compare(Edge fir, Edge sec) {
  return fir.w < sec.w;
}
void AddEdge(int u, int v) {
  e[++ edge_num].v = v, e[edge_num].u = u;
  e[edge_num].ne = head[u], head[u] = edge_num;
}
int Find(int x) {
  return bel[x] == x ? x : bel[x] = Find(bel[x]);
}
void Unico(int x, int y, int w) {
  int r1 = Find(x), r2 = Find(y);
  root = ++ node_num;
  bel[r1] = root, bel[r2] = root; //
  h[root] = 0; hard[root] = w;
  AddEdge(root, r1), AddEdge(root, r2);
}
void Dfs(int u) {
  dfn[u] = ++ Dfn, 
  data[Dfn] = val[Dfn] = h[u];
  size[u] = 1;
  for (int i = 1; (1 << i) <= dep[u]; ++ i) {
    fa[u][i] = fa[fa[u][i - 1]][i - 1];
  }
  for (int i = head[u]; i; i = e[i].ne) {
    dep[e[i].v] = dep[u] + 1, fa[e[i].v][0] = u;
    Dfs(e[i].v);
    size[u] += size[e[i].v];
  }
}
void Build(int &now, int L, int R) {
  now = ++ tree_node_num;
  if (L >= R) return ;
  int mid = (L + R) >> 1;
  Build(ls, L, mid), Build(rs, mid + 1, R);
}
void Update(int pre, int &now, int L, int R, int value) {
  now = ++ tree_node_num;
  t[now].size = t[pre].size + 1;
  ls = t[pre].lson, rs = t[pre].rson;
  if (L >= R) return ;
  int mid = (L + R) >> 1;
  if (value > mid) Update(rs, rs, mid + 1, R, value);
  else Update(ls, ls, L, mid, value);
}
int Query(int l, int r, int L, int R, int key) {
  if(L == R) return L;
  int sz = t[t[r].lson].size - t[t[l].lson].size; //
  int mid = (L + R) >> 1;
  if(key <= sz) return Query(t[l].lson, t[r].lson, L, mid, key);
  return Query(t[l].rson, t[r].rson, mid + 1, R, key - sz);
}
//=============================================================
int main() {  
  node_num = n = read(), m = read(), q = read();
  for (int i = 1; i <= n; ++ i) h[i] = read();
  for (int i = 1; i <= m; ++ i) {
    e1[i].u = read(), e1[i].v = read(), e1[i].w = read();
  }
  std ::sort(e1 + 1, e1 + m + 1, Compare);
  for (int i = 1; i <= n + m; ++ i) bel[i] = i;
  for (int i = 1; i <= m; ++ i) {
    int u = e1[i].u, v = e1[i].v, w = e1[i].w;
    if (Find(u) == Find(v)) continue ;
    Unico(u, v, w);
  }
  // for (int i = 1; i <= edge_num; ++ i) {
  //   printf("%d ", e[i].u);
  //   printf("%d\n", e[i].v);
  // }
  // for (int i = 1; i <= node_num; ++ i) printf("%d ", hard[i]);
  // printf("\n\n\n");
  Dfs(root);
  // for (int i = 1; i <= node_num; ++ i) printf("%d ", val[i]);
  // printf("\n\n\n");
  std :: sort(data + 1, data + node_num + 1);
  int lth = std :: unique(data + 1, data + node_num + 1) - data - 1;
  for (int i = 1; i <= node_num; ++i) {
    int ori = val[i];
    val[i] = std :: lower_bound(data + 1, data + lth + 1, val[i]) - data;
    map[val[i]] = ori;
  }
  // for (int i = 1; i <= node_num; ++ i) printf("%d ", val[i]);
  // printf("\n\n\n");
  Build(Root[0], 1, node_num);
  for (int i = 1; i <= node_num; ++ i) {
    Update(Root[i - 1], Root[i], 1, node_num, val[i]);
  }
  
  for (int i = 1; i <= q; ++ i) {
    int v = read(), x = read(), k = read(), rt = v;
    for (int j = 20; j >= 0; j --) {
      if (fa[rt][j]  && hard[fa[rt][j]]<= x) {
        rt = fa[rt][j];
      }
    }
    int ans = Query(Root[dfn[rt] - 1], Root[dfn[rt] + size[rt] - 1], 1, node_num, size[rt] - k);
    printf("%d\n", map[ans - 1] == 0 ? - 1 : map[ans - 1]);
  }
  return 0;
}
posted @ 2020-08-31 14:38  Luckyblock  阅读(184)  评论(0)    收藏  举报