2023.03.29总结

发布时间 2023-04-03 13:35:26作者: xiehanrui0817

题目1:洛谷 P2024

题意

  • \(n\) 个动物,每个动物都是 \(A,B,C\) 中的一种,其中 \(A\)\(B\)\(B\)\(C\)\(C\)\(A\)。给定两种食物链关系。

    • 第一种说法是 1 X Y,表示 \(X\)\(Y\) 是同类。
    • 第二种说法是 2 X Y,表示 \(X\)\(Y\)
  • 这两种关系有 \(k\) 条,一条关系是真话满足当前的话与前面所有的真话冲突不冲突,且 \(X,Y \le n\)\(X \ne Y\)。请输出假话的数量。

  • \(1 \le n \le 5 \times 10^4,1 \le k \le 10^5\)

思路

  • 这道题显然是并查集。令 \(i\) 对应的集合为与动物 \(i\) 同类的动物, \(i + n\) 对应的集合为 \(i\) 吃的动物, \(i + n + n\) 对应的集合为吃 \(i\) 的动物。

  • 如果当前关系为第一种,这条关系为真必须满足(为假答案数 \(+1\),以下只考虑了第一种情况,后两种自己判一下):

    • \(X\) 对应的集合不是 \(Y + n\) 对应的集合。
    • \(Y\) 对应的集合不是 \(X + n\) 对应的集合。
  • 否则如果是真话需满足:

    • \(X\) 对应的集合不是 \(Y + n\) 对应的集合。
    • \(Y\) 对应的集合不是 \(Y\) 对应的集合。
  • 如果当前关系为第一种且为真,将以下集合合并(为假答案数 \(+1\),以下只考虑了第一种情况,后两种自己判一下):

    • \(X\) 对应的集合不是 \(Y\) 对应的集合。
    • \(X + n\) 对应的集合不是 \(Y + n\) 对应的集合。
    • \(X + n\) 对应的集合不是 \(Y + n + n\) 对应的集合。
  • 如果当前关系为第二种种且为真,将以下集合合并:

    • \(X + n\) 对应的集合不是 \(Y\) 对应的集合。
    • \(X\) 对应的集合不是 \(Y + n + n\) 对应的集合。
    • \(X + n + n\) 对应的集合不是 \(Y + n\) 对应的集合。
  • 最后输出答案数即可。

  • 时间复杂度

    • 并查集维护合并操作:\(O(n \times log \ n)\)
    • \(k\) 次询问:\(O(k)\)
    • 总时间复杂度:\(O(n \times log \ n +k)\)
const int MAXN = 5e4 + 5;

int n, q, c, op, x, y, fa[MAXN * 3];

int Find(int x){
  return (fa[x] ? fa[x] = Find(fa[x]) : x);
}

int main(){
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> q;
  for(int i = 1; i <= q; i++){
    cin >> op >> x >> y;
    if(x > n || y > n || (x == y && op == 2)){ //特判后两种情况
      c++;
      continue;
    }
    int xa = Find(x), ya = Find(y); // x, y 对应的集合的代表
    int xb = Find(x + n), yb = Find(y + n); // x + n, y + n 对应的集合的代表
    int xc = Find(x + n + n), yc = Find(y + n + n); // x + n + n, y = n + n 对应的集合的代表
    if(op == 1){
      if(xa == yb || xb == ya){ //假话
        c++;
      }else{ //真话
        fa[xa] = (xa == ya ? 0 : ya);
        fa[xb] = (xb == yb ? 0 : yb);
        fa[xc] = (xc == yc ? 0 : yc);
      }
    }else{
      if(xa == ya || yb == xa){ //假话
        c++;
      }else{ //真话
        fa[ya] = (ya == xb ? 0 : xb);
        fa[yb] = (xc == yb ? 0 : xc);
        fa[xa] = (xa == yc ? 0 : yc);
      }
    }
  }
  cout << c;
  return 0;
}

题目2:CSES 2101

题意

  • \(n\) 个点,\(m\) 条无向边边和 \(q\) 组询问。每组询问给定两个点 \(x,y\),问 \(x,y\) 在加入第几条边后两个点从不连通到连通,如果加入 \(m\) 条边后仍不连通,输出 \(-1\)

  • \(1 \le n, m, q \le 2 \times 10^5\)

思路

  • 题意简化为:第 \(i\) 的边权为 \(i\),每组询问查询并查集建出森林后 \(x\)\(x,y\) 的最近公共祖先 \(z\) 的路径上的边权最大值和 \(y\)\(z\) 的路径上的边权最大值的最大值。

  • 找最近公共祖先可以下操作(找最近公共祖先时可以顺便求一下答案):

    • \(x\) 的子树大小 \(\le\) \(y\) 的子树大小,\(x\) 变为 \(x\) 的父亲。否则 \(y\) 变为 \(y\) 的父亲。直到 \(x = y\)
    • 如果 \(x\)\(y\) 都是某个树的根但是还有往上走,就说明原先的 \(x,y\) 不在同一个树内。
  • 时间复杂度

    • 输入 \(m\) 条道路:\(O(m)\)
    • 并查集维护合并集合:\(O(n \times log \ n)\)
    • \(q\) 组询问,每组询问 \(x,y\) 最多往上跳 \(log \ n\) 次,时间复杂度:\(O(q \times log \ n)\)
    • 总时间复杂度:\(O(m + (n + q) \times log \ n)\)