1. 程式人生 > 實用技巧 >CentOS7安裝Python3.6

CentOS7安裝Python3.6

原題連結

看到\(\operatorname{mex}\),一個顯然的想法就是根號分治。我們設\(d_u\)為點\(u\)的度數,再設一個合適正整數\(L\),代表根號分治的臨界值。將所有的點分為兩個集合\(S_1,S_2\),使得\(\forall u\in S_1,d_u\le L;\forall u\in S_2,d_u>L\)。有一個顯然的性質是\(|S_2|\le \big\lfloor \dfrac{2\times n}{L}\big\rfloor\)

先考慮查詢。對於所有\(S_1\)中的點,因其鄰居的個數最多是\(L\),所以珂以暴力列舉鄰居算\(\operatorname{mex}\)

。這個演算法是珂以做到\(O(L)\)的(答案輸出的\(\operatorname{mex}\)的最大值只能是\(n\),所以你珂以把所有大於\(n\)的數設定成\(n\))。對於所有\(S_2\)中的點,你就必須要對每個\(S_2\)中的點用一個奇技淫巧的資料結構來維護了。這個資料結構必須要支援全域性查詢\(\operatorname{mex}\)

先不談這個資料結構要實現哪些東西,我們來看看修改。修改是單點修改。一個點的修改只會影響到它周邊鄰居的答案。設點\(u\)的鄰居的集合為\(C_u\)。對於集合\(S_1\cap C_u\)中的所有點,珂以不用管它們,因為我們在詢問時是暴力列舉計算的。所以我們只要考慮集合\(S_2\cap C_u\)

中的所有點,這些點的個數最多也只有\(\big\lfloor \dfrac{2\times n}{L}\big\rfloor\)個。所以我們珂以暴力列舉這樣的鄰居,然後對於每一個鄰居,用維護它的那個資料結構來修改。具體來說就是在這個資料結構中刪除原有的數,再插入新新增的數。

所以這個資料結構要支援的就是:1. 插入; 2. 刪除; 3. 查詢全域性\(\operatorname{mex}\)。那麼問題來了,這個資料結構是啥呢?顯然平衡樹啊!每個節點維護兩個值:\(val, cnt\)。這樣詢問時就直接二分,判斷ls->size是否等於this->cnt即珂。

所以查詢時的時間複雜度為\(O(L+\log n)\)

,修改的時間複雜度為\(O(\big\lfloor \dfrac{2\times n}{L}\big\rfloor \log n)\)。顯然,由均值不等式,當\(L\)\(\sqrt {2\times n\log n}\)時,時間複雜度取到最優值為\(O(n\sqrt{n\log n})\)。(然後窩這個複雜度跑了\(655\)ms,勇獲rnk\(2\),吊打了一堆\(O(n\sqrt{n\log n})\)\(O(n\sqrt{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);
  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\n", i);
            break;
          }
        for (int &v: g[u])
          appear[a[v]] = false;
      }
      else {
        printf("%d\n", 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;

}