20200918 专题:Kruskal重构树

总览:

处理当一个图只能经过边权大于(小于)某个权值的边的问题
对图做最小生成树,当合并两个点集时,建立新结点,并将联通两个点集的最小边权作为点权,将这个点作为原来两个点集的父亲在这里插入图片描述
树的节点树为 2 n − 1 2n-1 2n1
树上的叶节点为原图的点,一个非叶节点代表原图中一个最小生成树
原图任意两个点路径上边权的最大值为它们树上的 LCA 的点权
以每个节点为起点,走边权不超过一定值的边所能到达的点集为树上某一结点的子树,dfn 连续

T1 P4768 [NOI2018]归程

思路:
先跑最短路,然后建树,节点上维护子树叶节点的 m i n d i s min_{dis} mindis,查询就倍增到能到达的最浅节点,然后输出 m i n d i s min_{dis} mindis

代码:

#include <bits/stdc++.h>
using namespace std;

#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define pb push_back
#define mp make_pair

namespace IO {
char _buf[1 << 21], *_p1 = _buf, *_p2 = _buf;
#define ch()                                                                 \
  (_p1 == _p2 &&                                                             \
           (_p2 = (_p1 = _buf) + fread(_buf, 1, 1 << 21, stdin), _p1 == _p2) \
       ? EOF                                                                 \
       : *_p1++)
inline int in() {
  int s = 0, f = 1;
  char x = ch();
  for (; x < '0' || x > '9'; x = ch())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = ch()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}
char buf_[1 << 21];
int p1_ = -1;
inline void flush() {
  fwrite(buf_, 1, p1_ + 1, stdout);
  p1_ = -1;
}
inline void pc(char x) {
  if (p1_ == (1 << 21) - 1) flush();
  buf_[++p1_] = x;
}
inline void out(int x) {
  char k[30];
  int pos = 0;
  if (!x) {
    pc('0');
    return;
  }
  if (x < 0) {
    pc('-');
    x = -x;
  }
  while (x) {
    k[++pos] = (x % 10) | 48;
    x /= 10;
  }
  for (int i = pos; i; i--) pc(k[i]);
  return;
}
inline void out(string x) {
  int k = x.size();
  for (int i = 0; i < k; i++) pc(x[i]);
}
}  // namespace IO
using namespace IO;

const int A = 5e5 + 5;
const int logA = 20;
const int INF = 1e9 + 5;
int n, m;
int dis[A], val[A];
vector<int> rt;

namespace Tree {
int head[A], tot_road;
struct Road {
  int nex, to;
} road[2 * A];
inline void edge(int x, int y) {
  road[++tot_road] = {head[x], y}, head[x] = tot_road;
}

int lg[A];
int dep[A], st[A][logA];
inline void DFS(int x) {
  for (int i = 1; i <= lg[dep[x]]; i++) st[x][i] = st[st[x][i - 1]][i - 1];
  for (int y = head[x]; y; y = road[y].nex) {
    int z = road[y].to;
    dep[z] = dep[x] + 1;
    st[z][0] = x;
    DFS(z);
    dis[x] = min(dis[x], dis[z]);
  }
  return;
}

inline int qurey(int x, int v) {
  for (int i = lg[dep[x]]; ~i; i--)
    if (val[st[x][i]] > v) x = st[x][i];
  return dis[x];
}

inline void work() {
  lg[1] = 0;
  for (int i = 2; i <= (n << 1); i++) lg[i] = lg[i >> 1] + 1;
  for (int i = 0; i < rt.size(); i++) {
    dep[rt[i]] = 1;
    DFS(rt[i]);
  }
  int Q = in(), K = in(), S = in(), ans = 0;
  while (Q--) {
    int x = in(), y = in();
    x = (x + K * ans - 1) % n + 1, y = (y + K * ans) % (S + 1);
    out(ans = qurey(x, y)), pc('\n');
  }
  return;
}

}  // namespace Tree

namespace T {
int head[A], tot_road;
struct Road {
  int nex, to, w;
} road[2 * A];
inline void edge(int x, int y, int w) {
  road[++tot_road] = {head[x], y, w}, head[x] = tot_road;
}

struct node {
  int x, y, w;
  inline friend bool operator<(node u, node v) { return u.w > v.w; }
} p[A];

int ex[A];
inline void djk() {
  for (int i = 1; i <= (n << 1); i++) dis[i] = INF, ex[i] = 0;
  priority_queue<pair<int, int> > q;
  dis[1] = 0;
  q.push(mp(-dis[1], 1));
  while (!q.empty()) {
    int x = q.top().second;
    q.pop();
    if (ex[x]) continue;
    ex[x] = 1;
    for (int y = head[x]; y; y = road[y].nex) {
      int z = road[y].to, w = road[y].w;
      if (dis[z] > dis[x] + w) {
        dis[z] = dis[x] + w;
        q.push(mp(-dis[z], z));
      }
    }
  }
  return;
}

int all;
int f[A];
inline int find(int x) { return f[x] == x ? f[x] : f[x] = find(f[x]); }
inline void build() {
  sort(p + 1, p + 1 + m);
  all = n;
  for (int i = 1; i <= (n << 1); i++) f[i] = i;
  for (int i = 1; i <= m; i++) {
    int x = p[i].x, y = p[i].y;
    if (find(x) == find(y)) continue;
    val[++all] = p[i].w;
    Tree::edge(all, find(x)), Tree::edge(all, find(y));
    f[find(x)] = all, f[find(y)] = all;
  }
  for (int i = 1; i <= all; i++)
    if (find(i) == i) rt.pb(i);
  return;
}

inline void work() {
  n = in(), m = in();
  for (int i = 1; i <= m; i++) {
    int u = in(), v = in(), l = in(), a = in();
    edge(u, v, l), edge(v, u, l);
    p[i].x = u, p[i].y = v, p[i].w = a;
  }
  djk();
  build();
  return;
}
}  // namespace T

inline void clean() {
  T::tot_road = 0;
  memset(T::head, 0, sizeof(T::head));
  rt.clear();
  Tree::tot_road = 0;
  memset(Tree::head, 0, sizeof(Tree::head));
  memset(Tree::st, 0, sizeof(Tree::st));
  return;
}

signed main() {
  int T = in();
  while (T--) {
    clean();
    T::work();
    Tree::work();
  }
  flush();
  return 0;
}

T2 P4197 Peaks

思路:
建树后就是查询子树内第 k k k 高的高度
dfn 序上维护主席树即可

代码:

#include <bits/stdc++.h>
using namespace std;

#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define pb push_back
#define mp make_pair

namespace IO {
char _buf[1 << 21], *_p1 = _buf, *_p2 = _buf;
#define ch()                                                                 \
  (_p1 == _p2 &&                                                             \
           (_p2 = (_p1 = _buf) + fread(_buf, 1, 1 << 21, stdin), _p1 == _p2) \
       ? EOF                                                                 \
       : *_p1++)
inline int in() {
  int s = 0, f = 1;
  char x = ch();
  for (; x < '0' || x > '9'; x = ch())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = ch()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}
char buf_[1 << 21];
int p1_ = -1;
inline void flush() {
  fwrite(buf_, 1, p1_ + 1, stdout);
  p1_ = -1;
}
inline void pc(char x) {
  if (p1_ == (1 << 21) - 1) flush();
  buf_[++p1_] = x;
}
inline void out(int x) {
  char k[30];
  int pos = 0;
  if (!x) {
    pc('0');
    return;
  }
  if (x < 0) {
    pc('-');
    x = -x;
  }
  while (x) {
    k[++pos] = (x % 10) | 48;
    x /= 10;
  }
  for (int i = pos; i; i--) pc(k[i]);
  return;
}
inline void out(string x) {
  int k = x.size();
  for (int i = 0; i < k; i++) pc(x[i]);
}
}  // namespace IO
using namespace IO;

const int A = 5e5 + 5;
const int INF = 1e9 + 5;
const int logA = 20;
int n, m, Q;
int hi[A], val[A], all;
int ex[A];
vector<int> root;

namespace Tree {
int head[A], tot_road;
struct Road {
  int nex, to;
} road[2 * A];
inline void edge(int x, int y) {
  road[++tot_road] = {head[x], y}, head[x] = tot_road;
}
int lg[A], f[A][logA];
int dep[A], sz[A], pos[A], idx[A], ed[A], tot;
struct SGT {
  int ls, rs;
  int num;
} tr[4 * logA * A];
int rt[A], tot_;
inline void insert(int &x, int y, int l, int r, int v) {
  x = ++tot_;
  tr[x] = tr[y];
  tr[x].num++;
  if (l == r) return;
  int mid = (l + r) >> 1;
  if (v <= mid)
    insert(tr[x].ls, tr[y].ls, l, mid, v);
  else
    insert(tr[x].rs, tr[y].rs, mid + 1, r, v);
  return;
}
inline int Kth(int x, int y, int l, int r, int k) {
  if (l == r) return l;
  int mid = (l + r) >> 1;
  int num = tr[tr[y].rs].num - tr[tr[x].rs].num;
  if (k <= num)
    return Kth(tr[x].rs, tr[y].rs, mid + 1, r, k);
  else
    return Kth(tr[x].ls, tr[y].ls, l, mid, k - num);
}
inline void DFS(int x) {
  pos[x] = ++tot, idx[pos[x]] = x;
  for (int i = 1; i <= lg[dep[x]]; i++) f[x][i] = f[f[x][i - 1]][i - 1];
  for (int y = head[x]; y; y = road[y].nex) {
    int z = road[y].to;
    dep[z] = dep[x] + 1;
    f[z][0] = x;
    DFS(z);
    sz[x] += sz[z];
  }
  if (!sz[x]) sz[x] = 1;
  ed[x] = tot;
  return;
}
inline int qurey(int x, int v, int k) {
  for (int i = lg[dep[x]]; ~i; i--)
    if (val[f[x][i]] <= v) x = f[x][i];
  if (sz[x] < k) return -1;
  return Kth(rt[pos[x] - 1], rt[ed[x]], 0, INF, k);
}
inline void work() {
  lg[1] = 0;
  for (int i = 2; i <= all; i++) lg[i] = lg[i >> 1] + 1;
  for (int i = 0; i < root.size(); i++) {
    dep[root[i]] = 1;
    DFS(root[i]);
  }
  for (int i = 1; i <= tot; i++) {
    rt[i] = rt[i - 1];
    if (hi[idx[i]]) insert(rt[i], rt[i - 1], 0, INF, hi[idx[i]]);
  }
  while (Q--) {
    int x = in(), v = in(), k = in();
    out(qurey(x, v, k)), pc('\n');
  }
  return;
}
}  // namespace Tree

namespace T {
struct Road {
  int x, y, w;
  inline friend bool operator<(Road u, Road v) { return u.w < v.w; }
} p[A];
int f[A];
inline int find(int x) { return f[x] == x ? f[x] : f[x] = find(f[x]); }
inline void work() {
  n = in(), m = in(), Q = in();
  for (int i = 1; i <= n; i++) hi[i] = in();
  for (int i = 1; i <= m; i++) p[i].x = in(), p[i].y = in(), p[i].w = in();
  sort(p + 1, p + 1 + m);
  for (int i = 1; i <= (n << 1); i++) f[i] = i;
  all = n;
  for (int i = 1; i <= m; i++) {
    int x = p[i].x, y = p[i].y;
    if (find(x) == find(y)) continue;
    val[++all] = p[i].w;
    Tree::edge(all, find(x)), Tree::edge(all, find(y));
    f[find(x)] = f[find(y)] = all;
  }
  val[0] = INF;
  for (int i = 1; i <= n; i++)
    if (!ex[find(i)]) {
      root.pb(find(i));
      ex[find(i)] = 1;
    }
  return;
}
}  // namespace T

signed main() {
  T::work();
  Tree::work();
  flush();
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值