树上 分块 莫队 题型小结 加 题集

前言

最近接触到了树分块(大三才接触到了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 
#include 
#include 
#include 
#include 
#include 

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 
#include 
#include 
#include 

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 
#include 
#include 
#include 
#include 

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 
#include 
#include 
#include 
#include 
#include 

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 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]);
  }
}

你可能感兴趣的:(模板,树,分块)