zoukankan      html  css  js  c++  java
  • ICPC Central Europe Regional Contest 2019-I-Saba1000kg

    题目传送门

    sol:因为查询的点一共最多只有$100000$个。若当本次查询的点比较少,小于$sqrt{100000}$,那我们可以枚举起始点$u$和终点$v$,若$u, v$之间有边,则用并查集将$u, v$节点合并,最后统计有多少个联通块;若当本次查询的点比较多,大于$sqrt{100000}$,那我们可以枚举所有边,若$u, v$都在本次查询的点集内,则用并查集将$u, v$节点合并,最后统计有多少个联通块。

    • 二分+并查集
      #include <bits/stdc++.h>
      using namespace std;
      typedef long long LL;
      typedef pair<int, int> PII;
      const int MAXN = 100010;
      inline int read() {
          int n = 0, f = 1; char c = getchar();
          while (c < '0' || c > '9') {
              if (c == '-') f = -f;
              c = getchar();
          }
          while (c >= '0' && c <= '9') {
              n = 10 * n + (c ^ '0');
              c = getchar();
          }
          return f * n;
      }
      vector<int> edge[MAXN];
      int node[MAXN], dsu[MAXN];
      int find(int i) {
          if (dsu[i] == -1) return i;
          return dsu[i] = find(dsu[i]);
      }
      bool search_1(int u, int v) {
          int l = -1, r = edge[u].size();
          while (l < r - 1) {
              int m = l + r >> 1;
              if (edge[u][m] == v) return true;
              if (edge[u][m] < v) l = m;
              if (edge[u][m] > v) r = m;
          }
          return false;
      }
      bool search_2(int l, int r, int k) {
          if (node[l] == k || node[r] == k) return true;
          while (l < r - 1) {
              int m = l + r >> 1;
              if (node[m] == k) return true;
              if (node[m] < k) l = m;
              if (node[m] > k) r = m;
          }
          return false;
      }
      void slove_1(int n, int k) {
          for (int i = 1; i <= k; i++) {
              for (int j = 1; j < i; j++) {
                  if (search_1(node[i], node[j])) {
                      int fu = find(node[i]);
                      int fv = find(node[j]);
                      if (fu != fv) dsu[fu] = fv;
                  }
              }
          }
          int ans = 0;
          for (int i = 1; i <= k; i++) {
              if (dsu[node[i]] == -1) ans ++;
              else dsu[node[i]] = -1;
          }
          printf("%d
      ", ans);
      }
      int slove_2(int n, int k) {
          for (int u = 1; u <= n; u++) {
              for (int v : edge[u]) {
                  if (search_2(1, k, u) && search_2(1, k, v)) {
                      int fu = find(u);
                      int fv = find(v);
                      if (fu != fv) dsu[fu] = fv;
                  }
              }
          }
          int ans = 0;
          for (int i = 1; i <= k; i++) {
              if (dsu[node[i]] == -1) ans ++;
              else dsu[node[i]] = -1;
          }
          printf("%d
      ", ans);
      }
      int main() {
          int n = read(), m = read(), q = read();
          for (int i = 1; i <= m; i++) {
              int u = read(), v = read();
              edge[u].push_back(v);
              edge[v].push_back(u);
          }
          for (int i = 1; i <= n; i++) {
              sort(edge[i].begin(), edge[i].end());
          }
          memset(dsu, -1, sizeof(dsu));
          while (q--) {
              int k = read();
              for (int i = 1; i <= k; i++) node[i] = read();
              sort(node + 1, node + 1 + k);
              if (k <= 320) slove_1(n, k);
              else slove_2(n, k);
          }
          return 0;
      }

      ------------------------------------------------------------分隔线------------------------------------------------------------

      总结:这道题的解法真妙,一道题里用到了两种不同策略。$solve1$的复杂度是$O(k^2log_2k)$,当k比较小时非常快,$slove2$的复杂度是$O(mlog_2k)$,随着$k$的增加变化不大。所以$k$小用$slove1$,$k$大用$slove2$。

  • 相关阅读:
    微信公众平台消息接口开发之校验签名与消息响应合并
    微信公众平台开发之在网页上添加分享到朋友圈,关注微信号等按钮
    微信公众平台自定义菜单PHP开发
    所有边权均不相同的无向图最小生成树是唯一的证明
    无向带权图的最小生成树算法——Prim及Kruskal算法思路
    排序二叉树,平衡二叉树和红黑树的概念以及相关的操作讲解
    B树、B-树、B+树、B*树
    森林、树与二叉树相互转换
    普通树转换成二叉树
    哈夫曼树
  • 原文地址:https://www.cnblogs.com/Angel-Demon/p/12911549.html
Copyright © 2011-2022 走看看