Overcooked(并查集、线段树)

简介: 笔记

Overcooked!


传送门

题目描述

20.png

输入描述

21.png

输出描述

22.png

样例

输入


5 5

1 1 2

3 1 2

2 2 4

3 1 4

3 2 5


输出


YES

YES

NO


并查集

题解

题意:


读入 op,x,y


op==1: x 与 y 合 并

op==2: x 到 y 之 间 合 并

op==3: 询 问 x 与 y 是 否 一 个 集 合


思路


1、3操作容易,重点就在2操作,如果正常用并查集拉x、y范围会TLE。


所以在这里,加上一个数组ne 优化。ne[x] 表示 x 向右的第一个不在同一个集合中的数。


对于 n e 的初始化,ne[i]=i+1。在执行op==2时,ne[] 可以快速跨越[x,y] 区间并且并到一个集合中。


代码


int n, m;
int fa[N], ne[N];
int find(int x) {
  return fa[x] = fa[x] == x ? x : find(fa[x]);
}
void solve() {
  cin >> n >> m;
  for (int i = 1; i <= n; i++) fa[i] = i, ne[i] = i + 1;
  while(m--) {
    int op, x, y;
    cin >> op >> x >> y;
    if(op == 1) {
      fa[find(x)] = find(y);
    } else if(op == 2) {
      while(x <= y) {
        int t = ne[x];
        fa[find(x)] = find(y);
        ne[x] = ne[y];
        x = t;
      }
    } else {
      if(find(x) == find(y)) cout << "YES" << endl;
      else cout << "NO" << endl;
    }
  }
}

线段树

题解

思路

  • 区间修改+单点修改
  • 单点查询

代码

#include <bits/stdc++.h>
using namespace std;
#define ls u<<1
#define rs u<<1|1
const int N = 1000010;
struct node {
  int l, r;
  int v, add;
} tr[N << 2];
int n, m;
int fa[N];
int find(int x) {
  return fa[x] = fa[x] == x ? x : find(fa[x]);
}
void pushup(int u) {
  if(tr[ls].v == tr[rs].v) tr[u].v = tr[ls].v;
}
void pushdown(int u) {
  if(tr[u].add) {
    tr[ls].v = tr[rs].v = tr[ls].add = tr[rs].add = tr[u].add;
    tr[u].add = 0;
  }
}
void build(int u, int l, int r) {
  tr[u] = {l, r};
  if(l == r) { tr[u].v = fa[l]; return ; }
  int mid = l + r >> 1;
  build(ls, l, mid), build(rs, mid + 1, r);
  pushup(u);
}
void modify(int u, int l, int r, int v) {
  if(tr[u].l >= l && tr[u].r <= r) {
    tr[u].v = tr[u].add = v;
    return ;
  }
  int mid = tr[u].l + tr[u].r >> 1;
  // pushdown(u);
  if(l <= mid) modify(ls, l, r, v);
  if(r > mid) modify(rs, l, r, v);
  pushup(u);
}
int query(int u, int x) {
  if(tr[u].l == tr[u].r) return tr[u].v;
  int mid = tr[u].l + tr[u].r >> 1;
  pushdown(u);
  if(x <= mid) return query(ls, x);
  return query(rs, x);
}
int main() {
  cin >> n >> m;
  for (int i = 1; i <= n; i++) fa[i] = i;
  build(1, 1, n);
  int op, x, y;
  int a, b;
  for (int i = 1; i <= m; i++) {
    cin >> op >> x >> y;
    a = query(1, x);
    b = query(1, y);
    if(op == 1) {
      if(find(a) != find(b)) {
        fa[find(a)] = find(b);
        modify(1, x, x, find(b));
        // modify(1, y, y, find(b));
      }
    } else if(op == 2) {
      if(find(a) != find(b)) fa[find(a)] = find(b);
      modify(1, x, y, find(b));
    } else {
      if(find(a) != find(b)) cout << "NO" << endl;
      else cout << "YES" << endl;
    }
  }
  return 0;
}
相关文章
|
7月前
|
算法
并查集,路径压缩
并查集,路径压缩
47 0
|
7月前
线段树(连载中)
线段树(连载中)
38 0
并查集及其应用
并查集及其应用
69 0
|
7月前
|
算法 测试技术 C++
|
7月前
线段树最大子段
线段树最大子段
初识线段树
初识线段树
56 0
|
存储 算法 iOS开发
并查集详解及应用
并查集详解及应用
3854 0
|
存储 算法 Java
线段树SegmentTree
线段树SegmentTree
|
存储 Python
【23. 并查集】
**用途**: - 将俩个集合合并 - 询问俩个元素是否在一个集合当中 **基本原理**: - 每个集合用一棵树来表示。树根的编号就是整个集合的编号,每个节点存储它的父节点,`p[x]`表示x的父节点。
124 0
【23. 并查集】