20200803 专题:LCT

总览:

splay 动态维护树的信息
支持加边断边,换根等操作
可以维护连通性

板子:

struct LCT {
  int ch[2], f;
  int rev;
} tr[A];

#define ls(x) tr[x].ch[0]
#define rs(x) tr[x].ch[1]
inline int isroot(int x) { return ls(tr[x].f) != x && rs(tr[x].f) != x; }
inline void pushup(int x) {
  //
  return;
}
inline void reverse(int x) {
  if (x) swap(ls(x), rs(x)), tr[x].rev ^= 1;
}
inline void pushdown(int x) {
  if (tr[x].rev) reverse(ls(x)), reverse(rs(x)),tr[x].rev ^= 1;
}
inline void rotate(int x) {
  int y = tr[x].f, z = tr[y].f;
  int k = (rs(y) == x);
  if (z && !isroot(y)) tr[z].ch[rs(z) == y] = x;
  tr[x].f = z, tr[y].ch[k] = tr[x].ch[k ^ 1];
  tr[tr[x].ch[k ^ 1]].f = y;
  tr[x].ch[k ^ 1] = y, tr[y].f = x;
  pushup(y);
  return;
}
int st[A], top;
inline void pushpath(int x) {
  top = 0;
  st[++top] = x;
  for (int i = x; !isroot(i); i = tr[i].f) st[++top] = tr[i].f;
  for (int i = top; i; i--) pushdown(st[i]);
  return;
}
inline void splay(int x) {
  pushpath(x);
  while (!isroot(x)) {
    int y = tr[x].f, z = tr[y].f;
    if (!isroot(y)) {
      if ((rs(y) == x) == (rs(z) == y))
        rotate(y);
      else
        rotate(x);
    }
    rotate(x);
  }
  pushup(x);
  return;
}
inline void access(int x) {
  for (int y = 0; x; y = x, x = tr[x].f) splay(x), rs(x) = y, pushup(x);
}
inline int findroot(int x) {
  access(x), splay(x);
  while (ls(x)) pushdown(x), x = ls(x);
  return x;
}
inline void makeroot(int x) { access(x), splay(x), reverse(x); }
inline void split(int x, int y) { makeroot(x), access(y), splay(y); }
inline void link(int x, int y) {
  makeroot(x);
  if (findroot(y) != x) tr[x].f = y;
}
inline void cut(int x, int y) {
  makeroot(x);
  if (findroot(y) == x && tr[x].f == y && ls(y) == x) tr[x].f = 0, ls(y) = 0;
  pushup(y);
}
#undef ls
#undef rs

splaypushdown,改变儿子后 pushup

T1 P2147 [SDOI2008]洞穴勘测

思路:
LCT 维护连通性板子

代码:

#include <bits/stdc++.h>
using namespace std;
#define re register
namespace IO {
inline char ch() {
  static char buf[1 << 21], *p1 = buf, *p2 = buf;
  return 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;
  for (x = getchar(); x < '0' || x > '9'; x = getchar())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = getchar()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}
}  // namespace IO
using namespace IO;

const int A = 4e5 + 5;
int n, m;
struct LCT {
  int ch[2], f, rev;
  LCT() { ch[0] = ch[1] = f = rev = 0; }
} tr[A];

#define ls(x) tr[x].ch[0]
#define rs(x) tr[x].ch[1]
inline int isroot(int x) {
  return !(tr[x].f && (ls(tr[x].f) == x || rs(tr[x].f) == x));
}
inline void reverse(int x) {
  if (x) {
    swap(ls(x), rs(x));
    tr[x].rev ^= 1;
  }
  return;
}
inline void pushdown(int x) {
  if (tr[x].rev) {
    reverse(ls(x));
    reverse(rs(x));
    tr[x].rev ^= 1;
  }
  return;
}
inline void rotate(int x) {
  int y = tr[x].f, z = tr[y].f;
  int k = (rs(y) == x);
  if (!isroot(y)) tr[z].ch[rs(z) == y] = x;
  tr[x].f = z;
  tr[y].ch[k] = tr[x].ch[k ^ 1];
  if (tr[x].ch[k ^ 1]) tr[tr[x].ch[k ^ 1]].f = y;
  tr[x].ch[k ^ 1] = y, tr[y].f = x;
  return;
}
int st[A], top;
inline void pushpath(int x) {
  top = 0;
  st[++top] = x;
  for (int i = x; !isroot(i); i = tr[i].f) st[++top] = tr[i].f;
  for (int i = top; i; i--) pushdown(st[i]);
  return;
}
inline void splay(int x) {
  pushpath(x);
  while (!isroot(x)) {
    int y = tr[x].f, z = tr[y].f;
    if (!isroot(y)) {
      if ((rs(y) == x) == (rs(z) == y))
        rotate(y);
      else
        rotate(x);
    }
    rotate(x);
  }
  return;
}
inline void access(int x) {
  for (int y = 0; x; y = x, x = tr[x].f) splay(x), rs(x) = y;
  return;
}
inline int findroot(int x) {
  access(x), splay(x), pushdown(x);
  while (ls(x)) x = ls(x), pushdown(x);
  return x;
}
inline void makeroot(int x) {
  access(x), splay(x), reverse(x);
  return;
}
inline void split(int x, int y) {
  makeroot(x), access(y), splay(y);
  return;
}
inline void link(int x, int y) {
  makeroot(x);
  if (findroot(y) != x) tr[x].f = y;
  return;
}
inline void cut(int x, int y) {
  makeroot(x);
  if (findroot(y) == x && tr[x].f == y && ls(y) == x) tr[x].f = 0, ls(y) = 0;
  return;
}
#undef ls
#undef rs

signed main() {
  n = in(), m = in();
  while (m--) {
    char x = getchar();
    while (x != 'Q' && x != 'C' && x != 'D') x = getchar();
    if (x == 'Q') {
      int u = in(), v = in();
      makeroot(u);
      puts(findroot(v) == u ? "Yes" : "No");
    } else if (x == 'C') {
      int u = in(), v = in();
      link(u, v);
    } else {
      int u = in(), v = in();
      cut(u, v);
    }
  }
  return 0;
}

T2 P3690 【模板】Link Cut Tree (动态树)

思路:板子

代码:

#include <bits/stdc++.h>
using namespace std;
#define re register
namespace IO {
inline char ch() {
  static char buf[1 << 21], *p1 = buf, *p2 = buf;
  return 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;
  for (x = ch(); 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;
}
}  // namespace IO
using namespace IO;

const int A = 3e5 + 5;
int n, m;
struct LCT {
  int ch[2], f, rev;
  int val, sum;
} tr[A];

#define ls(x) tr[x].ch[0]
#define rs(x) tr[x].ch[1]
inline int isroot(int x) { return !(ls(tr[x].f) == x || rs(tr[x].f) == x); }

inline void pushup(int x) {
  tr[x].sum = tr[ls(x)].sum ^ tr[rs(x)].sum ^ tr[x].val;
  return;
}

inline void reverse(int x) {
  if (x) {
    swap(ls(x), rs(x));
    tr[x].rev ^= 1;
  }
  return;
}

inline void pushdown(int x) {
  if (tr[x].rev) {
    reverse(ls(x)), reverse(rs(x));
    tr[x].rev ^= 1;
  }
  return;
}

inline void rotate(int x) {
  int y = tr[x].f, z = tr[y].f;
  int k = (rs(y) == x);
  if (!isroot(y)) tr[z].ch[rs(z) == y] = x;
  tr[x].f = z, tr[y].ch[k] = tr[x].ch[k ^ 1];
  tr[tr[x].ch[k ^ 1]].f = y;
  tr[x].ch[k ^ 1] = y, tr[y].f = x;
  pushup(y), pushup(x);
  return;
}

int st[A], top;
inline void pushpath(int x) {
  top = 0;
  st[++top] = x;
  for (int i = x; !isroot(i); i = tr[i].f) st[++top] = tr[i].f;
  for (int i = top; i; i--) pushdown(st[i]);
  return;
}

inline void splay(int x) {
  pushpath(x);
  while (!isroot(x)) {
    int y = tr[x].f, z = tr[y].f;
    if (!isroot(y)) {
      if ((rs(y) == x) == (rs(z) == y))
        rotate(y);
      else
        rotate(x);
    }
    rotate(x);
  }
  pushup(x);
  return;
}

inline void access(int x) {
  for (int y = 0; x; y = x, x = tr[x].f) splay(x), rs(x) = y, pushup(x);
  return;
}

inline int findroot(int x) {
  access(x), splay(x);
  while (ls(x)) pushdown(x), x = ls(x);
  return x;
}

inline void makeroot(int x) {
  access(x), splay(x), reverse(x);
  return;
}

inline void split(int x, int y) {
  makeroot(x), access(y), splay(y);
  return;
}

inline void link(int x, int y) {
  makeroot(x);
  if (findroot(y) != x) tr[x].f = y;
  return;
}

inline void cut(int x, int y) {
  makeroot(x);
  if (findroot(y) == x && tr[x].f == y && ls(y) == x) tr[x].f = 0, ls(y) = 0;
  pushup(y);
  return;
}
#undef ls
#undef rs

signed main() {
  n = in(), m = in();
  for (int i = 1; i <= n; i++) tr[i].val = in();
  while (m--) {
    int opt = in();
    if (opt == 0) {
      int u = in(), v = in();
      split(u, v);
      printf("%d\n", tr[v].sum);
    } else if (opt == 1) {
      int u = in(), v = in();
      link(u, v);
    } else if (opt == 2) {
      int u = in(), v = in();
      cut(u, v);
    } else {
      int u = in(), v = in();
      splay(u);
      tr[u].val = v;
    }
  }
  return 0;
}

T3 P2387 [NOI2014]魔法森林

思路:
考虑类似最小生成树的做法,加边动态维护
先将边按一种边权排序
依次加边维护另一种边权的最小生成树
每次更新答案
可以用 LCT 维护最小生成树
如果成环就找到环上边权最大的边比较大小
需要将边化作点,如果加入边,就将这个点和两端点相连

代码:

#include <bits/stdc++.h>
using namespace std;
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 = getchar();
  while (x < '0' || x > '9') {
    x = getchar();
    if (x == '-') f = -1;
  }
  while (x >= '0' && x <= '9') {
    s = (s * 10) + (x & 15);
    x = getchar();
  }
  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) {
  if (!x) {
    pc('0');
    return;
  }
  if (x < 0) {
    pc('-');
    x = -x;
  }
  char k[20];
  int tot = 0;
  while (x) {
    k[++tot] = (x % 10) | 48;
    x /= 10;
  }
  for (int i = tot; i; i--) pc(k[i]);
  return;
}
inline void out(string x) {
  int len = x.size();
  for (int i = 0; i < len; i++) pc(x[i]);
}
}  // namespace IO
using namespace IO;

const int A = 2e5 + 5;
const int INF = 1e9;
int n, m;
struct Road {
  int x, y, a, b;
} p[A];
inline bool cmp(Road u, Road v) { return u.b < v.b; }
inline int d(int x) { return x + n; }

int f[A];
inline int find(int x) { return f[x] == x ? f[x] : f[x] = find(f[x]); }
inline void merge(int x, int y) {
  int xx = find(x), yy = find(y);
  if (xx != yy) f[xx] = yy;
  return;
}

struct LCT {
  int ch[2], f;
  int rev, val, pos;
} tr[A];

#define ls(x) tr[x].ch[0]
#define rs(x) tr[x].ch[1]
#define max(x) tr[tr[x].pos].val
inline int isroot(int x) { return ls(tr[x].f) != x && rs(tr[x].f) != x; }
inline void pushup(int x) {
  tr[x].pos = x;
  if (ls(x) && max(ls(x)) > max(x))
    tr[x].pos = tr[ls(x)].pos;
  if (rs(x) && max(rs(x)) > max(x))
    tr[x].pos = tr[rs(x)].pos;
  return;
}
inline void reverse(int x) {
  if (x) {
    swap(ls(x), rs(x));
    tr[x].rev ^= 1;
  }
}
inline void pushdown(int x) {
  if (tr[x].rev) {
    reverse(ls(x)), reverse(rs(x));
    tr[x].rev ^= 1;
  }
  return;
}
inline void rotate(int x) {
  int y = tr[x].f, z = tr[y].f;
  int k = (rs(y) == x);
  if (!isroot(y)) tr[z].ch[rs(z) == y] = x;
  tr[x].f = z, tr[y].ch[k] = tr[x].ch[k ^ 1];
  tr[tr[x].ch[k ^ 1]].f = y;
  tr[x].ch[k ^ 1] = y, tr[y].f = x;
  pushup(y), pushup(x);
  return;
}
int st[A], top;
inline void pushpath(int x) {
  top = 0;
  st[++top] = x;
  for (int i = x; !isroot(i); i = tr[i].f) st[++top] = tr[i].f;
  for (int i = top; i; i--) pushdown(st[i]);
  return;
}
inline void splay(int x) {
  pushpath(x);
  while (!isroot(x)) {
    int y = tr[x].f, z = tr[y].f;
    if (!isroot(y)) {
      if ((rs(y) == x) == (rs(z) == y))
        rotate(y);
      else
        rotate(x);
    }
    rotate(x);
  }
  pushup(x);
  return;
}
inline void access(int x) {
  for (int y = 0; x; y = x, x = tr[x].f) splay(x), rs(x) = y, pushup(x);
}
inline int findroot(int x) {
  access(x), splay(x);
  while (ls(x)) pushdown(x), x = ls(x);
  pushdown(x);
  return x;
}
inline void makeroot(int x) { access(x), splay(x), reverse(x); }
inline void split(int x, int y) { makeroot(x), access(y), splay(y); }
inline void link(int x, int y) {
  makeroot(x);
  if (findroot(y) != x) tr[x].f = y;
  return;
}
inline void cut(int x, int y) {
  makeroot(x);
  if (findroot(y) == x && tr[x].f == y && ls(y) == x) tr[x].f = 0, ls(y) = 0;
  pushup(y);
  return;
}
inline int qurey(int x, int y) {
  split(x, y);
  return tr[y].pos;
}
#undef ls
#undef rs
#define max

int ans = INF;

signed main() {
  n = in(), m = in();
  for (int i = 1; i <= m; i++)
    p[i].x = in(), p[i].y = in(), p[i].a = in(), p[i].b = in();
  sort(p + 1, p + 1 + m, cmp);
  for (int i = 1; i <= n + m; i++) f[i] = i, tr[i].pos = i;
  for (int i = 1; i <= m; i++) tr[d(i)].val = p[i].a;
  for (int i = 1; i <= m; i++) {
    int x = p[i].x, y = p[i].y, pos = 0;
    if (find(x) == find(y)) {
      int w = qurey(x, y);
      if (tr[w].val > p[i].a)
        cut(p[w - n].x, w), cut(w, p[w - n].y);
      else
        pos = 1;
    } else {
      merge(x, y);
    }
    if (!pos) link(x, d(i)), link(d(i), y);
    if (find(1) == find(n)) ans = min(ans, p[i].b + tr[qurey(1, n)].val);
  }
  if (ans == INF)
    out(-1);
  else
    out(ans);
  pc('\n');
  flush();
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值