zoukankan      html  css  js  c++  java
  • [HNOI2016]最小公倍数

    题面

    ( ext{Solution:})

    显然在线算法根本不可做,先将询问离线,按 (b) 排序。

    注意到不一定是简单路径,所以一个询问回答为 (Yes) 当且仅当 (u,v) 在同一个连通块中且该连通块中边最大值 (a) 与最大值 (b) 与询问的 (a,b) 相等。

    所以我们考虑用带权并查集维护联通块。

    所以我们将询问也按 (b) 排序,对于一个询问 (q[i]) , 将 (b<q[i].b) 的边塞入并查集中,再将 (a<q[i].a) 的边也塞入并查集中,这样并查集中的边就是所有 (a<q[i].a,b<q[i].b) ,然后记录答案,记录完后将刚刚插入的 (a<q[i].a) 的边再撤销,继续处理下一个询问。

    由于要支持撤销操作,用按秩合并,不能路径压缩。

    由于b有序且单调,所以插入 (b)(O(n)) ,插入 (a) 与撤销 (a)(O(nlogn)) 的,总复杂度 (O(n^2logn))

    然后我们发现复杂度过高的瓶颈在于 (a) 的插入是 (O(nlogn)) 的而且插入了许多没有必要插入的信息(那些远小于 (q[i].a) 的边), 考虑对 (a) 分块,将复杂度均摊。

    (a) 排序,分块,对于一个块 (x) ,它所对应的并查集包含了 (ale a[size * x]) 的所有边。

    当我们插入一条 (ble q[i].b) 的边时,需要二分查找到这条边 (a) 所在的块,然后将该块一直到最后一个块的并查集中都插入这一条边,对于 (le q[i].a) 的边,我们就可以找到最大的开头 (le q[i].a) 的块,然后将这块开头到 (q[i].a) 都塞进这一块所对应的并查集(因为在这块之前的块里的边显然都小于 $q[i].a $),查询也在这一块中进行(显然 (b<q[i].b) 的边都在这块的并查集里).

    总复杂度 (O(nlognsqrt{n})​).

    #include <set>
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <assert.h>
    #include <algorithm>
    
    using namespace std;
    
    #define LL long long
    #define debug(...) fprintf(stderr, __VA_ARGS__)
    #define GO debug("GO
    ")
    
    inline int rint() {
      register int x = 0, f = 1; register char c;
      while (!isdigit(c = getchar())) if (c == '-') f = -1;
      while (x = (x << 1) + (x << 3) + (c ^ 48), isdigit(c = getchar()));
      return x * f;
    }
    
    template<typename T> inline void chkmin(T &a, T b) { a > b ? a = b : 0; }
    template<typename T> inline void chkmax(T &a, T b) { a < b ? a = b : 0; }
    
    
    const int N = 1e5 + 100;
    
    struct Edge {
    
      int u, v, a, b, id;
    
      bool operator< (const Edge &B) const {
        return a < B.a;
      }
    
    } fir[N<<1], sec[N<<1], q[N<<1];
    
    bool cmp_a(const Edge &a, const Edge &b) {
      return a.a < b.a;
    }
    
    bool cmp_b(const Edge &a, const Edge &b) {
      return a.b < b.b;
    }
    
    int top;
    
    struct Ifm {
    
      int type, x, y, z;
    
    } stk[N<<1];
    
    struct Union_Set {
    
      int fa[N], size[N], max_a[N], max_b[N];
    
      int Find(int x) {
        while (fa[x]) x = fa[x];
        return x;
      }
    
      void Merge(int x, int y, int a, int b, int op) {
    
        int Fx = Find(x), Fy = Find(y);
    
        if (size[Fx] > size[Fy]) 
          swap(Fx, Fy), swap(x, y);
    
        if (op == 1)
          stk[++top] = (Ifm) { 1, Fy, max_a[Fy], max_b[Fy] };
    
        chkmax(max_a[Fy], max(max_a[Fx], a));
        chkmax(max_b[Fy], max(max_b[Fx], b));
    
        if (Fx == Fy) 
          return;
    
        fa[Fx] = Fy;
        size[Fy] += size[Fx];
        if (op == 1)
          stk[++top] = (Ifm) { 0, Fx, Fy, size[Fx] };
      }
    
      void Backdate() {
        while (top) {
          if (stk[top].type == 0) {
    
            fa[stk[top].x] = 0;
            size[stk[top].y] -= stk[top].z;
    
          } else {
    
            max_a[stk[top].x] = stk[top].y;
            max_b[stk[top].x] = stk[top].z;
    
          }
          --top;
        }
      }
    
      int Query(int x, int y, int a, int b) {
    
        int anc = Find(x);
    
        if (x == y and a == 0 and b == 0)
          return size[anc] != 1;
    
        if (anc != Find(y)) 
          return 0;
    
        if (max_a[anc] != a or max_b[anc] != b) 
          return 0;
    
        return 1;
      }
    } U[1005];
    
    int n, m, K;
    int ans[N], front[N];
    
    int main() {
    #ifndef ONLINE_JUDGE
      freopen("xhc.in", "r", stdin);
      freopen("xhc.out", "w", stdout);
    #endif
      n = rint(), m = rint();
    
      for (int i = 1; i <= m; ++ i) {
        fir[i].u = rint();
        fir[i].v = rint();
        fir[i].a = rint();
        fir[i].b = rint();
        sec[i] = fir[i];
      }
    
      K = rint();
      for (int i = 1; i <= K; ++ i) {
        q[i].u = rint();
        q[i].v = rint();
        q[i].a = rint();
        q[i].b = rint();
        q[i].id = i;
      }
    
      int SIZE = sqrt(m * 20), Block_Cnt = m / SIZE;
    
      for (int i = 0; i <= Block_Cnt; ++ i) 
        for (int j = 1; j <= n; ++ j)
          U[i].size[j] = 1;
    
      sort(fir + 1, fir + 1 + m, cmp_a);
      sort(sec + 1, sec + 1 + m, cmp_a);
      for (int i = 0; i <= Block_Cnt; ++ i)
        front[i] = sec[i * SIZE].a;
      sort(q + 1, q + 1 + K, cmp_b);
      sort(sec + 1, sec  + 1 + m, cmp_b);
    
      int pos = 1;
      for (int i = 1; i <= K; ++ i) {
    
        while (sec[pos].b <= q[i].b and pos <= m) {
          int block = lower_bound(front, front + 1 + Block_Cnt, sec[pos].a) - front;
    
          for (int j = block; j <= Block_Cnt; ++ j) {
            U[j].Merge(sec[pos].u, sec[pos].v, sec[pos].a, sec[pos].b, 0);
          }
    
          pos++;
        }
    
        int P = upper_bound(front, front + Block_Cnt + 1, q[i].a) - front - 1;
    
        Edge tp; tp.a = front[P];
        int tpos = upper_bound(fir + 1, fir + 1 + m, tp) - fir;
    
        while (fir[tpos].a <= q[i].a and tpos <= m) {
          if (fir[tpos].b <= q[i].b) {
            U[P].Merge(fir[tpos].u, fir[tpos].v, fir[tpos].a, fir[tpos].b, 1);
          }
          ++tpos;
        }
    
        ans[q[i].id] = U[P].Query(q[i].u, q[i].v, q[i].a, q[i].b);
        U[P].Backdate();
      }
    
      for (int i = 1; i <= K; ++ i)
        ans[i] == 1 ? puts("Yes") : puts("No");
      return 0;
    }
    
  • 相关阅读:
    基于tensorflow的MNIST手写数字识别(二)--入门篇
    怎么自行HTTP的POST包头,需要使用json
    本地数据库(SQL Server)远程连接服务器端服务器
    在 Visual Studio 2010 中创建 ASP.Net Web Service
    VMware加载vmdk文件
    Android典型界面设计(5)——使用SlidingMenu和DrawerLayout分别实现左右侧边栏
    FatSecret Platform API
    Android典型界面设计(4)——使用ActionBar+Fragment实现tab切换
    IOS UITableView删除功能
    Android GUI之View测量
  • 原文地址:https://www.cnblogs.com/cnyali-Tea/p/10614100.html
Copyright © 2011-2022 走看看