• ICPC Central Europe Regional Contest 2019ISaba1000kg Jathon


    题目传送门

    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\n", 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\n", 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^2\log_2k)$,当k比较小时非常快,$slove2$的复杂度是$O(m\log_2k)$,随着$k$的增加变化不大。所以$k$小用$slove1$,$k$大用$slove2$。

  • 相关阅读:
    错误网络异常:android.os.NetworkOnMainThreadException
    方法服务android学习笔记42_服务的生命周期
    芯片软件随想录(关于核心技术)
    数组比特【编程珠玑】如何优化程序打印出小于100000的素数
    宋体函数Java乔晓松oracle分组函数
    调试客户端windbg远程调试方法
    方法对象Hibernate联合主键
    文件运行跟踪 root.sh 的 执行过程
    移植交叉编译pcDuino + busybox 成功搭建最小linux系统
    方法定制iOS学习笔记8UITableView的定制
  • 原文地址:https://www.cnblogs.com/Jathon-cnblogs/p/12911549.html
Copyright © 2020-2023  润新知