• HDU6756 Finding a MEX


    原题链接

    看到(operatorname{mex}),一个显然的想法就是根号分治。我们设(d_u)为点(u)的度数,再设一个合适正整数(L),代表根号分治的临界值。将所有的点分为两个集合(S_1,S_2),使得(forall uin S_1,d_ule L;forall uin S_2,d_u>L)。有一个显然的性质是(|S_2|le iglfloor dfrac{2 imes n}{L}ig floor)

    先考虑查询。对于所有(S_1)中的点,因其邻居的个数最多是(L),所以珂以暴力枚举邻居算(operatorname{mex})。这个算法是珂以做到(O(L))的(答案输出的(operatorname{mex})的最大值只能是(n),所以你珂以把所有大于(n)的数设置成(n))。对于所有(S_2)中的点,你就必须要对每个(S_2)中的点用一个奇技淫巧的数据结构来维护了。这个数据结构必须要支持全局查询(operatorname{mex})

    先不谈这个数据结构要实现哪些东西,我们来看看修改。修改是单点修改。一个点的修改只会影响到它周边邻居的答案。设点(u)的邻居的集合为(C_u)。对于集合(S_1cap C_u)中的所有点,珂以不用管它们,因为我们在询问时是暴力枚举计算的。所以我们只要考虑集合(S_2cap C_u)中的所有点,这些点的个数最多也只有(iglfloor dfrac{2 imes n}{L}ig floor)个。所以我们珂以暴力枚举这样的邻居,然后对于每一个邻居,用维护它的那个数据结构来修改。具体来说就是在这个数据结构中删除原有的数,再插入新添加的数。

    所以这个数据结构要支持的就是:1. 插入; 2. 删除; 3. 查询全局(operatorname{mex})。那么问题来了,这个数据结构是啥呢?显然平衡树啊!每个节点维护两个值:(val, cnt)。这样询问时就直接二分,判断ls->size是否等于this->cnt即珂。

    所以查询时的时间复杂度为(O(L+log n)),修改的时间复杂度为(O(iglfloor dfrac{2 imes n}{L}ig floor log n))。显然,由均值不等式,当(L)(sqrt {2 imes nlog n})时,时间复杂度取到最优值为(O(nsqrt{nlog n}))。(然后窝这个复杂度跑了(655)ms,勇获rnk(2),吊打了一堆(O(nsqrt{nlog n}))(O(nsqrt{n}))的,而且还没开Ofast)。

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    constexpr int _I_Buffer_Size = 32 << 20;
    static char _I_Buffer[_I_Buffer_Size];
    char *_I_pos = _I_Buffer;
    void read(int32_t *n) {
      while (*_I_pos < 48) _I_pos++;
      *n = *_I_pos++ - '0';
      while (*_I_pos > 47)
        *n = *n * 10 + (*_I_pos++ - '0');
    }
    
    constexpr int Maxn = 1e5 + 5, BL = 1024;
    
    int n, m, q;
    int a[Maxn];
    vector<int> g[Maxn];
    int deg[Maxn];
    __attribute__((__always_inline__)) __inline
    void add_edge(int eu, int ev) {
      g[eu].push_back(ev), deg[ev]++;
    }
    bool appear[Maxn];
    
    namespace treap {
      constexpr int Maxs = ::Maxn * 127;
      static mt19937 rng(40113143); // 20051212 + 20060617 + 1314
      int ls[Maxs], rs[Maxs], fix[Maxs], size[Maxs], tot;
      int val[Maxs], cnt[Maxs];
      __attribute__((__always_inline__)) __inline
      void init_treap() {
        tot = 0;
        ls[0] = rs[0] = 0;
        size[0] = 0, fix[0] = 0;
        val[0] = cnt[0] = 0;
      }
      __attribute__((__always_inline__)) __inline
      int newnode(int v) {
        ++tot;
        ls[tot] = rs[tot] = 0;
        fix[tot] = rng();
        size[tot] = 1;
        val[tot] = v;
        cnt[tot] = 1;
        return tot;
      }
      __attribute__((__always_inline__)) __inline
      void pushup(int p) {
        size[p] = size[ls[p]] + size[rs[p]] + 1;
      }
      int join(int l, int r) {
        if (!l || !r) return l | r;
        if (fix[l] < fix[r]) {
          rs[l] = join(rs[l], r);
          pushup(l);
          return l;
        }
        else {
          ls[r] = join(l, ls[r]);
          pushup(r);
          return r;
        }
      }
      void split_s(int p, int s, int &l, int &r) {
        if (!p) l = r = 0;
        else {
          int lsize = size[ls[p]] + 1;
          if (lsize <= s) {
            l = p;
            split_s(rs[p], s - lsize, rs[l], r);
            pushup(l);
          }
          else {
            r = p;
            split_s(ls[p], s, l, ls[r]);
            pushup(r);
          }
        }
      }
      void split_v(int p, int v, int &l, int &r) {
        if (!p) l = r = 0;
        else {
          if (val[p] <= v) {
            l = p;
            split_v(rs[p], v, rs[l], r);
            pushup(l);
          }
          else {
            r = p;
            split_v(ls[p], v, l, ls[r]);
            pushup(r);
          }
        }
      }
      void insert(int &p, int x) {
        int A, B, C;
        split_v(p, x - 1, A, B);
        split_v(B, x, B, C);
        if (size[B] > 0) cnt[B]++;
        else B = newnode(x);
        p = join(join(A, B), C);
      }
      void erase(int &p, int x) {
        int A, B, C;
        split_v(p, x - 1, A, B);
        split_v(B, x, B, C);
        if (--cnt[B] == 0) B = 0;
        p = join(join(A, B), C);
      }
      int mex(int p) {
        int c = 0;
        while (p) {
          int lsize = size[ls[p]] + 1;
          if (lsize + c == val[p] + 1) {
            c += lsize;
            p = rs[p];
          }
          else {
            p = ls[p];
          }
        }
        return c;
      }
    } // namespace treap
    using namespace treap;
    int root[Maxn];
    
    void re_clear() {
      for (int i = 1; i <= n; ++i) {
        g[i].clear();
        deg[i] = 0;
      }
      memset(root, 0, (n + 1) << 2);
    }
    
    void Main() {
    
      read(&n), read(&m);
      for (int i = 1; i <= n; ++i) {
        read(a + i);
        if (a[i] > n) a[i] = n + 1;
      }
      for (int i = 1; i <= m; ++i) {
        int u, v;
        read(&u), read(&v);
        add_edge(u, v);
        add_edge(v, u);
      }
    
      static const auto cmp_node =
      [&](int x, int y)->bool {
        return deg[x] > deg[y];
      };
      for (int i = 1; i <= n; ++i)
        sort(g[i].begin(), g[i].end(), cmp_node);
      init_treap();
      for (int u = 1; u <= n; ++u)
        if (deg[u] > BL) {
          for (int &v: g[u]) treap::insert(root[u], a[v]);
        }
    
      read(&q);
      while (q--) {
        int op, u, x;
        read(&op), read(&u);
        if (op == 1) {
          read(&x);
          if (x > n) x = n + 1;
          for (int &v: g[u]) {
            if (deg[v] <= BL) break;
            treap::erase(root[v], a[u]);
            treap::insert(root[v], x);
          }
          a[u] = x;
        }
        else {
          if (deg[u] <= BL) {
            for (int &v: g[u])
              appear[a[v]] = true;
            for (int i = 0; ; ++i)
              if (!appear[i]) {
                printf("%d
    ", i);
                break;
              }
            for (int &v: g[u])
              appear[a[v]] = false;
          }
          else {
            printf("%d
    ", treap::mex(root[u]));
          }
        }
      }
    
      re_clear();
    
    }
    
    int main() {
    
      fread(_I_Buffer, 1, _I_Buffer_Size, stdin);
    
      int tests;
      read(&tests);
      while (tests--) Main();
    
      return 0;
    
    }
    
    
  • 相关阅读:
    解决:std::ostream operator<< should have been declared inside 'xxx'
    c++ friend 遇到 namespace 无法访问 private 成员的问题
    Compiler Error C2872: ambiguous symbol
    【持续更新】总结:C++开发时积累的一些零碎的东西
    陷阱:C++模块之间的”直接依赖“和”间接依赖“与Makefile的撰写
    ZThread::ThreadLocal:ERROR C4716 must return a value的解决
    java值传递
    iframe与父页面传值
    iframe父子兄弟之间调用传值(contentWindow && parent)
    MySQL返回影响行数的测试示例
  • 原文地址:https://www.cnblogs.com/libra9z/p/13396825.html
Copyright © 2020-2023  润新知