Tree
树分块的方式¶
可以参考 真 - 树上莫队。
也可以参考 ouuan 的博客/莫队、带修莫队、树上莫队详解/树上莫队。
树上莫队同样可以参考以上两篇文章。
树分块的应用¶
树分块除了应用于莫队,还可以灵活地运用到某些树上问题中。但可以用树分块解决的题目往往都有更优秀的做法,所以相关的题目较少。
顺带提一句,“gty 的妹子树”的树分块做法可以被菊花图卡掉。
BZOJ4763 雪辉¶
先进行树分块,然后对每个块的关键点,预处理出它到祖先中每个关键点的路径上颜色的 bitset,以及每个关键点的最近关键点祖先,复杂度是 bitset
存下来的复杂度。
回答询问的时候,先从路径的端点暴力跳到所在块的关键点,再从所在块的关键点一块一块地向上跳,直到 bitset
已经预处理了,剩下的在暴力跳的过程中计算。单次询问复杂度是 bitset
的 count()
,求 bitset
的 _Find_first()
。
所以,总复杂度为
参考代码
#include <algorithm>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <iostream>
using namespace std;
int read() {
int out = 0;
char c;
while (!isdigit(c = getchar()))
;
for (; isdigit(c); c = getchar()) out = out * 10 + c - '0';
return out;
}
const int N = 100010;
const int B = 666;
const int C = 30000;
void add(int u, int v);
void dfs(int u);
int head[N], nxt[N << 1], to[N << 1], cnt;
int n, m, type, c[N], fa[N], dep[N], sta[N], top, tot, bl[N], key[N / B + 5],
p[N], keyid[N];
bool vis[N];
bitset<C> bs[N / B + 5][N / B + 5], temp;
int main() {
int i, u, v, x, y, k, lastans = 0;
n = read();
m = read();
type = read();
for (i = 1; i <= n; ++i) c[i] = read();
for (i = 1; i < n; ++i) {
u = read();
v = read();
add(u, v);
add(v, u);
}
dfs(1);
if (!tot) ++tot;
if (keyid[key[tot]] == tot) keyid[key[tot]] = 0;
key[tot] = 1;
keyid[1] = tot;
while (top) bl[sta[top--]] = tot;
for (i = 1; i <= tot; ++i) { // 预处理
if (vis[key[i]]) continue;
vis[key[i]] = true;
temp.reset();
for (u = key[i]; u; u = fa[u]) {
temp[c[u]] = 1;
if (keyid[u]) {
if (!p[key[i]] && u != key[i]) p[key[i]] = u;
bs[keyid[key[i]]][keyid[u]] = temp;
}
}
}
while (m--) {
k = read();
temp.reset();
while (k--) {
u = x = read() ^ lastans;
v = y = read() ^ lastans;
while (key[bl[x]] != key[bl[y]]) {
if (dep[key[bl[x]]] > dep[key[bl[y]]]) {
if (x == u) { // 若是第一次跳先暴力跳到关键点
while (x != key[bl[u]]) {
temp[c[x]] = 1;
x = fa[x];
}
} else
x = p[x]; // 否则跳一整块
} else {
if (y == v) {
while (y != key[bl[v]]) {
temp[c[y]] = 1;
y = fa[y];
}
} else
y = p[y];
}
}
if (keyid[x]) temp |= bs[keyid[key[bl[u]]]][keyid[x]];
if (keyid[y]) temp |= bs[keyid[key[bl[v]]]][keyid[y]];
while (x != y) {
if (dep[x] > dep[y]) {
temp[c[x]] = 1;
x = fa[x];
} else {
temp[c[y]] = 1;
y = fa[y];
}
}
temp[c[x]] = true;
}
int ans1 = temp.count(), ans2 = (~temp)._Find_first();
printf("%d %d\n", ans1, ans2);
lastans = (ans1 + ans2) * type;
}
return 0;
}
void dfs(int u) {
int i, v, t = top;
for (i = head[u]; i; i = nxt[i]) {
v = to[i];
if (v == fa[u]) continue;
fa[v] = u;
dep[v] = dep[u] + 1;
dfs(v);
if (top - t >= B) {
key[++tot] = u;
if (!keyid[u]) keyid[u] = tot;
while (top > t) bl[sta[top--]] = tot;
}
}
sta[++top] = u;
}
void add(int u, int v) {
nxt[++cnt] = head[u];
head[u] = cnt;
to[cnt] = v;
}
BZOJ4812 由乃打扑克¶
这题和上一题基本一样,唯一的区别是得到 bitset
后如何计算答案。
由于 BZOJ 是计算所有测试点总时限,不好卡,所以可以用 _Find_next()
水过去。
正解是每 bitset
,因为标准库的 bitset
不能取某
代码可以参考 这篇博客。
buildLast update and/or translate time of this article,Check the history
editFound smelly bugs? Translation outdated? Wanna contribute with us? Edit this Page on Github
peopleContributor of this article ouuan, Ir1d, Marcythm, Xeonacid
translateTranslator of this article Visit the original article!
copyrightThe article is available under CC BY-SA 4.0 & SATA ; additional terms may apply.