樹上 分塊 莫隊 題型小結 加 題集

前言

最近接觸到了樹分塊(大三才接觸到了QAQ),然後打算總結下……

普通莫隊算法

簡單回顧一下普通的莫隊算法,莫隊算法是用來解決區間詢問的算法,其把區間分成n 份,每份的大小是n 。把所有詢問按其左端點所在區間爲第一關鍵字,右端點按第二關鍵字排序,然後依次移動左右指針,處理相關詢問。算法的時間複雜度在於當前區間的左右指針移動。左指針每次只會在塊內移動,複雜度爲O(qn) ,因爲相同塊的詢問的右指針是遞增的,所以每塊的右指針總共會移動n次,共有n 塊,所以右指針的複雜度是O(nn) 。總複雜度爲O(nn)

靜態樹上莫隊

cf上有個博客說的很清楚(雖然是英文)地址:http://codeforces.com/blog/entry/43230,博客的大意就是說通過dfs序,把一顆n個節點的樹變成一個2 * n的dfs序數組,該數組下標是dfs序,內容是dfs序對應的樹上的節點。然後通過判斷(u, v)的lca,就可以判斷出u到v的路徑。

具體操作是:設in[x]爲節點x的入度,out[x]爲節點x的出度,令in[u] < in[v],如果u就是(u, v)的lca,那麼這個路徑就是dfs序數組的in[u]到in[v]的下標,如果u不是(u, v)的lca,那麼這個路徑是dfs數組的out[u]到in[v]的下標外加lca。

爲什麼?不爲什麼,這就是dfs序的性質。

複雜度O(nn)

spoj上的COT2就是這樣的問題:http://www.spoj.com/problems/COT2/

代碼見後面第1份代碼

帶修改的普通莫隊

瞭解了靜態的樹上莫隊,只要知道帶修改的普通莫隊,就可以寫出帶修改的樹上莫隊了。

面對一個普通的單點修改的區間詢問問題,可以把整個區間分成n1/3 塊,每塊的大小是n2/3 ,然後對每個詢問,按其左端點所在區間編號爲第一關鍵字,右端點所在區間爲第二關鍵字,詢問的時間爲第三關鍵字排序。然後依次處理詢問就好了,這樣排序後的詢問根據左右端點所處區間不同被分成了n1/3n1/3=n2/3 個不同區間,每個區間修改操作各有q次,總共修改了qn2/3 次,每個區間的左右端點都只會移動n2/3 距離,所以左右指針移動的總體複雜度是O(qn2/3) ,時間複雜度O(n5/3)

bzoj 2120:http://www.lydsy.com/JudgeOnline/problem.php?id=2120

代碼見後面第2份代碼

其實自己感覺大多數不在樹上的查詢問題,都有比莫隊更優的方法?樹上的分塊由於要判斷一個樹鏈上的點是否在集合裏,所以一定要有一個O(n)的數組用來判斷,此時莫隊纔在時空上更優秀(求指正)。

帶修改的樹上莫隊

把上述兩個方法放在一起,就是帶修改的樹上莫隊了。

UOJ 58:http://uoj.ac/problem/58?locale=en

代碼見後面第3份代碼

另一種用到了分塊思想的樹鏈詢問處理

還有這麼一類問題,一條給定的樹鏈,要求你在這個樹鏈上k步k步跳着走,然後詢問經過的點上各個點權的相關問題(如經過點的異或和,經過所有點的最大值)

HDOJ 5840,以及計蒜課 xor

對於這種問題,如果k = 1,那麼就成了一個樹鏈剖分問題,又考慮如果k很大的情況,那麼沒幾步我們就能跳完所有樹鏈。

分別考慮上述兩種情況:

  1. k很小,那麼極端情況是k = 1,需要一個線段樹之類的結構來維護出各種信息,可以針對每個節點所在的dep % k值的不同,建立k棵不同的線段樹(實際操作中只要把相同顏色的點放在一起然後對整個線段建樹就好了),用以維護信息。查詢信息的時候,只要正常的樹鏈剖分,每次指定查詢哪一個顏色的線段樹就可以得到答案。
  2. k很大,極端情況k無窮大,一個節點都不用訪問,可以把比較大的k放在其指定的節點上,離線後利用dfs時維護的棧統計信息就好。

問題在於找到一個合適的分界點,把兩個k區分開,自己目前感覺k爲10左右的某個區分點可以得到較好的效果。

代碼見第4分代碼(感覺這篇博客應該拆成4篇寫的QAQ

代碼

spoj COT2 代碼

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

#define PB push_back

const int N = 4e4 + 5;
const int M = 1e5 + 5;

int pos[N<<1];

struct Query {
  int l, r, fa, id;
  bool operator<(const Query &rhs) {
    return pos[l] < pos[rhs.l] || (pos[l] == pos[rhs.l] && r < rhs.r);
  }
};

int n, m;
int lim, idx;
int res, ans[M];
int val[N], a[N<<1], hsh[N], tot;
int in[N], out[N];
int siz[N], son[N], dep[N], pa[N], top[N];
int cnt[N];
bool used[N];
vector<int> edges[N];
Query querys[M];

void dfs1(int u, int fa) {
  in[u] = ++idx;
  a[idx] = u;
  pa[u] = fa, siz[u] = 1; son[u] = 0; dep[u] = dep[fa] + 1;
  for (int v: edges[u]) {
    if (v == fa) continue;
    dfs1(v, u);
    siz[u] += siz[v];
    if (siz[son[u]] < siz[v]) son[u] = v;
  }
  out[u] = ++idx;
  a[idx] = u;
}

void dfs2(int u, int fa) {
  top[u] = fa;
  if (son[u]) dfs2(son[u], fa);
  for (int v: edges[u]) {
    if (v == pa[u] || v == son[u]) continue;
    dfs2(v, v);
  }
}

inline int lca(int u, int v) {
  for (; top[u] != top[v]; u = pa[top[u]]) if (dep[top[u]] < dep[top[v]]) swap(u, v);
  return dep[u] < dep[v] ? u : v;
}

inline void flip(int x) {
  if (used[x] && (--cnt[val[x]]) == 0) --res;
  else if (!used[x] && (cnt[val[x]]++) == 0) ++res;
  used[x] ^= true;
}

int main() {
  while (~scanf("%d%d", &n, &m)) {
    tot = 0;
    for (int i = 1; i <= n; ++i) {
      scanf("%d", val + i);
      hsh[tot++] = val[i];
      edges[i].clear();
      used[i] = false;
    }
    sort(hsh, hsh + tot);
    tot = unique(hsh, hsh + tot) - hsh;
    for (int i = 1; i <= n; ++i) val[i] = lower_bound(hsh, hsh + tot, val[i]) - hsh;
    int u, v, fa;
    for (int i = 1; i < n; ++i) {
      scanf("%d%d", &u, &v);
      edges[u].PB(v);
      edges[v].PB(u);
    }
    idx = 0;
    dfs1(1, 0);
    dfs2(1, 1);
    for (lim = 1; lim * lim < idx; ++lim);
    for (int i = 1; i <= idx; ++i) pos[i] = (i - 1) / lim + 1;
    for (int i = 0; i < m; ++i) {
      scanf("%d%d", &u, &v);
      if (in[u] > in[v]) swap(u, v);
      querys[i].id = i;
      fa = lca(u, v);
      if (fa == u) {
        querys[i].l = in[u]; querys[i].r = in[v]; querys[i].fa = 0;
      }
      else {
        querys[i].l = out[u]; querys[i].r = in[v]; querys[i].fa = in[fa];
      }
    }
    sort(querys, querys + m);
    int l = querys[0].l; int r = querys[0].l - 1;
    res = 0;
    for (int i = 0; i < tot; ++i) cnt[i] = 0;
    for (int i = 0; i < m; ++i) {
      while (l < querys[i].l) flip(a[l++]);
      while (l > querys[i].l) flip(a[--l]);
      while (r < querys[i].r) flip(a[++r]);
      while (r > querys[i].r) flip(a[r--]);
      if (querys[i].fa) flip(a[querys[i].fa]);
      ans[querys[i].id] = res;
      if (querys[i].fa) flip(a[querys[i].fa]);
    }
    for (int i = 0; i < m; ++i) printf("%d\n", ans[i]);
  }
}

bzoj 2120 代碼

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1e4 + 5;
const int M = 1e6 + 5;

int pos[N];

struct Query {
  int l, r, tim, id;
  Query(int l = 0, int r = 0, int tim = 0, int id = 0):
    l(l), r(r), tim(tim), id(id) {}
  bool operator<(const Query &rhs) const {
    if (pos[l] != pos[rhs.l]) return pos[l] < pos[rhs.l];
    if (pos[r] != pos[rhs.r]) return pos[r] < pos[rhs.r];
    return tim < rhs.tim;
  }
};

struct Modify {
  int pos, v;
  Modify(int pos=0, int v=0): pos(pos), v(v) {}
};

int n, q, m, block_siz;
int tot, tim, res, l, r, cur;
int val[N], ans[N];
int cnt[M];
char op[5];
Query querys[N];
Modify modifys[N];

inline void insert(int x) { res += (++cnt[x]) == 1; }
inline void erase(int x) { res -= (--cnt[x]) == 0; }
inline void change(int t) {
  int pos = modifys[t].pos, v = modifys[t].v;
  int VAL = val[pos];
  if (l <= pos && pos <= r) {
    insert(v);
    erase(VAL);
  }
  val[pos] = v; modifys[t].v = VAL;
}

int main() {
  while (~scanf("%d%d", &n, &q)) {
    for (block_siz = 1; block_siz * block_siz * block_siz < n; ++block_siz);
    block_siz *= block_siz;
    for (int i = 1; i <= n; ++i) {
      scanf("%d", val + i);
      pos[i] = (i - 1) / block_siz + 1;
    }
    int x, y;
    tim = tot = 0;
    for (int i = 1; i <= q; ++i) {
      scanf("%s%d%d", op, &x, &y);
      if (op[0] == 'Q') {
        querys[tot] = Query(x, y, tim, tot);
        ++tot;
      }
      else {
        modifys[++tim] = Modify(x, y);
      }
    }
    sort(querys, querys + tot);
    for (int i = 0; i < M; ++i) cnt[i] = 0;
    l = 1, r = 0, cur = 0;
    res = 0;
    for (int i = 0; i < tot; ++i) {
      while (cur < querys[i].tim) change(++cur);
      while (cur > querys[i].tim) change(cur--);
      while (r < querys[i].r) insert(val[++r]);
      while (r > querys[i].r) erase(val[r--]);
      while (l < querys[i].l) erase(val[l++]);
      while (l > querys[i].l) insert(val[--l]);
      ans[querys[i].id] = res;
    }
    for (int i = 0; i < tot; ++i) printf("%d\n", ans[i]);
  }
}

uoj 58 代碼

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

#define PB push_back

typedef long long LL;
const int N = 1e5 + 5;

int pos[N<<1], block_siz;

struct Query {
  int l, r, tim, id, fa;
  Query(int l = 0, int r = 0, int tim = 0, int id = 0, int fa = 0):
    l(l), r(r), tim(tim), id(id), fa(fa) {}
  bool operator<(const Query &rhs) const {
    if (pos[l] != pos[rhs.l]) return pos[l] < pos[rhs.l];
    if (pos[r] != pos[rhs.r]) return pos[r] < pos[rhs.r];
    return tim < rhs.tim;
  }
};

struct Modify {
  int pos, v;
  Modify(int pos = 0, int v = 0):
    pos(pos), v(v) {}
};

int n, m, q;
int v[N], w[N], c[N];
int pa[N], siz[N], dep[N], son[N], top[N], in[N], out[N], clk[N<<1], idx;
int cnt[N], tim, tot, l, r, cur;
LL res, ans[N];
bool used[N];
vector<int> edges[N];
Query querys[N];
Modify modifys[N];

void dfs1(int u, int fa) {
  in[u] = ++idx; clk[idx] = u;
  pa[u] = fa; siz[u] = 1; dep[u] = dep[fa] + 1; son[u] = 0;
  for (int v: edges[u]) {
    if (v == fa) continue;
    dfs1(v, u);
    siz[u] += siz[v];
    if (siz[son[u]] < siz[v]) son[u] = v;
  }
  out[u] = ++idx;
  clk[idx] = u;
}

void dfs2(int u, int fa) {
  top[u] = fa;
  if (son[u]) dfs2(son[u], fa);
  for (int v: edges[u]) {
    if (v == son[u] || v == pa[u]) continue;
    dfs2(v, v);
  }
}

inline int lca(int u, int v) {
  for (; top[u] != top[v]; u = pa[top[u]]) if (dep[top[u]] < dep[top[v]]) swap(u, v);
  return dep[u] < dep[v] ? u : v;
}

inline void flip(int x) {
  if (!used[x]) res += 1LL * v[c[x]] * w[++cnt[c[x]]];
  else res -= 1LL * v[c[x]] * w[cnt[c[x]]--];
  used[x] ^= true;
}

inline void change(int t) {
  int pos = modifys[t].pos, V = modifys[t].v; int C = c[pos];
  if (used[pos]) {
    res -= 1LL * v[C] * w[cnt[C]--];
    res += 1LL * v[V] * w[++cnt[V]];
  }
  c[pos] = V; modifys[t].v = C;
}

int main() {
  while (~scanf("%d%d%d", &n, &m, &q)) {
    for (int i = 1; i <= m; ++i) scanf("%d", v + i);
    for (int i = 1; i <= n; ++i) scanf("%d", w + i);
    for (int i = 1; i <= n; ++i) edges[i].clear();
    int u, v;
    for (int i = 1; i < n; ++i) {
      scanf("%d%d", &u, &v);
      edges[u].PB(v);
      edges[v].PB(u);
    }
    for (int i = 1; i <= n; ++i) scanf("%d", c + i);
    idx = 0;
    dfs1(1, 0);
    dfs2(1, 1);
    for (block_siz = 1; block_siz * block_siz * block_siz < idx; ++block_siz);
    block_siz *= block_siz;
    for (int i = 1; i <= idx; ++i) pos[i] = (i - 1) / block_siz + 1;
    int op, x, y, fa;
    tim = tot = 0;
    for (int i = 1; i <= q; ++i) {
      scanf("%d%d%d", &op, &x, &y);
      if (op) {
        if (in[x] > in[y]) swap(x, y);
        fa = lca(x, y);
        if (fa == x) {
          querys[tot] = Query(in[x], in[y], tim, tot);
          ++tot;
        }
        else {
          querys[tot] = Query(out[x], in[y], tim, tot, fa);
          ++tot;
        }
      }
      else {
        modifys[++tim] = Modify(x, y);
      }
    }
    sort(querys, querys + tot);
    for (int i = 1; i <= n; ++i) used[i] = false;
    for (int i = 1; i <= m; ++i) cnt[i] = 0;
    l = 1; r = 0; cur = 0; res = 0;
    for (int i = 0; i < tot; ++i) {
      while (r < querys[i].r) flip(clk[++r]);
      while (r > querys[i].r) flip(clk[r--]);
      while (l < querys[i].l) flip(clk[l++]);
      while (l > querys[i].l) flip(clk[--l]);
      while (cur < querys[i].tim) change(++cur);
      while (cur > querys[i].tim) change(cur--);
      if (querys[i].fa) flip(querys[i].fa);
      ans[querys[i].id] = res;
      if (querys[i].fa) flip(querys[i].fa);
    }
    for (int i = 0; i < tot; ++i) printf("%lld\n", ans[i]);
  }
}

HDU 5840 代碼

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

#define PB push_back

template<class T1, class T2> inline void gmax(T1 &a, T2 b) { if (a < b) a = b; }

typedef pair<int, int> P;
const int N = 1e5 + 5;
const int LIM = 500;
const int INF = 0x3f3f3f3f;

struct Query {
  int id, step, beg, tot;
  Query(int id = 0, int step = 0, int beg = 0, int tot = 0):
    id(id), step(step), beg(beg), tot(tot) {}
};

int lim;
int n, q;
int stk[N];
int val[N], ans[N];
int pa[N], dep[N], siz[N], son[N], top[N], id[N], rid[N], idx;
int seg_tree[1 << 18], base;
int seq[N], beg[LIM];
vector<int> edges[N], nums[LIM];
vector<Query> querys[N], small_query[LIM];

void dfs1(int u, int fa) {
  siz[u] = 1; son[u] = 0; pa[u] = fa;
  dep[u] = dep[fa] + 1;
  for (int v: edges[u]) {
    if (v == fa) continue;
    dfs1(v, u);
    siz[u] += siz[v];
    if (siz[son[u]] < siz[v]) son[u] = v;
  }
}

void dfs2(int u, int fa) {
  id[u] = ++idx; rid[idx] = u; top[u] = fa;
  if (son[u]) dfs2(son[u], fa);
  for (int v: edges[u]) {
    if (v == pa[u] || v == son[u]) continue;
    dfs2(v, v);
  }
}

inline int lca(int u, int v) {
  for (; top[u] != top[v]; u = pa[top[u]]) if (dep[top[u]] < dep[top[v]]) swap(u, v);
  return dep[u] < dep[v] ? u : v;
}

void dfsAns(int u, int fa) {
  stk[dep[u]] = val[u];
  Query q;
  for (int i = 0; i < querys[u].size(); ++i) {
    q = querys[u][i];
    while (q.beg <= q.tot) {
      gmax(ans[q.id], stk[dep[u] - q.beg]);
      q.beg += q.step;
    }
  }
  for (int v: edges[u]) {
    if (v == fa) continue;
    dfsAns(v, u);
  }
}

inline void update(int x, int v) {
  x += base;
  seg_tree[x] = v;
  for (x >>= 1; x; x >>= 1) seg_tree[x] = max(seg_tree[x << 1], seg_tree[x << 1 | 1]);
}

inline int segmentQuery(int l, int r) {
  int ret = -INF;
  l += base - 1; r += base + 1;
  for (; l ^ r ^ 1; l >>= 1, r >>= 1) {
    if (~ l & 1) gmax(ret, seg_tree[l ^ 1]);
    if (r & 1) gmax(ret, seg_tree[r ^ 1]);
  }
  return ret;
}

inline int QUERY(int l, int r, int col) {
  int tl = lower_bound(nums[col].begin(), nums[col].end(), l) - nums[col].begin() + beg[col];
  int tr = upper_bound(nums[col].begin(), nums[col].end(), r) - nums[col].begin() - 1 + beg[col];
  if (!(beg[col] <= tl && tl < beg[col + 1])) tl = 1e9;
  if (!(beg[col] <= tl && tl < beg[col + 1])) tr = -1;
  return tl <= tr ? segmentQuery(tl, tr) : 0;
}

inline int query(int u, int v, int k) {
  int fa = lca(u, v);
  int cu = (dep[u] + 1) % k;
  int cv = (dep[fa] + (k - (dep[u] - dep[fa] + 1) % k)) % k;
  int ret = -INF;
  while (top[u] != top[v]) {
    if (dep[top[u]] > dep[top[v]]) {
      gmax(ret, QUERY(id[top[u]], id[u], cu));
      u = top[u]; u = pa[u];
    }
    else {
      gmax(ret, QUERY(id[top[v]], id[v], cv));
      v = top[v]; v = pa[v];
    }
  }
  if (dep[u] > dep[v]) {
    gmax(ret, QUERY(id[v], id[u], cu));
  }
  else {
    gmax(ret, QUERY(id[u], id[v], cv));
  }
  return ret;
}

inline void solve(int k) {
  for (int i = 1; i <= n; ++i) {
    nums[dep[rid[i]] % k].PB(i);
  }
  for (int i = 0; i <= base << 1; ++i) seg_tree[i] = 0;
  idx = 0;
  for (int i = 0; i < k; ++i) {
    beg[i] = idx + 1;
    for (int t: nums[i]) {
      update(++idx, val[rid[t]]);
    }
  }
  beg[k] = idx + 1;
  int u, v;
  for (Query q: small_query[k]) {
    u = q.step; v = q.beg;
    ans[q.id] = query(u, v, k);
  }
  for (int i = 0; i < k; ++i) nums[i].clear();
}

int main() {
  int T, kase = 0;
  scanf("%d", &T);
//  read(T);
  while (T--) {
    scanf("%d%d", &n, &q);
    lim = 10;
//    read(n); read(q);
    for (int i = 1; i <= n; ++i) {
      edges[i].clear();
      querys[i].clear();
      scanf("%d", val + i);
//      read(val[i]);
    }
    int u, v, k, fa, l1, l2;
    for (int i = 1; i < n; ++i) {
      scanf("%d%d", &u, &v);
//      read(u); read(v);
      edges[u].PB(v);
      edges[v].PB(u);
    }
    idx = 0;
    dfs1(1, 0);
    dfs2(1, 1);
    for (base = 1; base <= n + 1; base <<= 1);
//    for (int i = 0; i <= base << 1; ++i) seg_tree[i] = 0;
//    for (int i = 1; i <= n; ++i) update(i, val[rid[i]]);
    for (int i = 1; i <= q; ++i) {
      scanf("%d%d%d", &u, &v, &k);
//      read(u); read(v); read(k);
      if (k <= lim) {
        small_query[k].PB(Query(i, u, v));
        continue;
      }
      ans[i] = 0;
      if (k > n) continue;
      fa = lca(u, v);
      l1 = dep[u] - dep[fa];
      l2 = dep[v] - dep[fa];
      if (fa ^ u) querys[u].PB(Query(i, k, k - 1, l1));
      if (fa ^ v) querys[v].PB(Query(i, k, (l1 + l2 + 1) % k, l2));
    }
    dfsAns(1, 0);
    for (int i = 1; i <= lim; ++i) if (small_query[i].size()) {
      solve(i);
      small_query[i].clear();
    }
    printf("Case #%d:\n", ++kase);
    for (int i = 1; i <= q; ++i) printf("%d\n", ans[i]);
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章