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  阅读(150)  评论(0编辑  收藏  举报