点分治与动态点分治略解
暑假的时候学的算法,太久不用就忘记了代码怎么写。
点分治
就大概的讲一下ba。
将一棵无根树转化为以重心为根的有根树,假设为\(p\),那么对于树上的路径,就可以分为两类:
- 经过根节点\(p\)
- 包含于\(p\)的某一棵子树内(不经过根节点)
由于重心的性质:
以重心为根,任意一棵子树的大小都不超过整棵树大小的一半。
所以保证了复杂度为\(O(N\log N)\)
对于第一种路径直接进行统计,对于第二种路径,则删除与重心相连的所有边,然后分治下去。
需要注意的是,代码里的solve
函数应该根据实际情况进行编写,而不是直接套模板。
#include <bits/stdc++.h>
using namespace std;
inline int ty() {
char ch = getchar();
int x = 0, f = 1;
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
const int _ = 1e5 + 10;
const int INF = 0x3f3f3f3f;
int tot, head[_], to[_ << 1], nxt[_ << 1], edge[_ << 1];
void adde(int x, int y, int z) {
to[++tot] = y;
edge[tot] = z;
nxt[tot] = head[x];
head[x] = tot;
}
int N, K;
int root, totsiz, mxsiz, siz[_], vis[_], ans;
int dis[_], cnt;
void getroot(int x, int fa) {
siz[x] = 1;
int maxx = 0;
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (vis[y] || y == fa) continue;
getroot(y, x);
siz[x] += siz[y];
maxx = max(maxx, siz[y]);
}
maxx = max(maxx, totsiz - siz[x]);
if (maxx < mxsiz) mxsiz = maxx, root = x;
}
void query(int x, int fa, int d) {
dis[++cnt] = d;
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i], z = edge[i];
if (vis[y] || y == fa) continue;
query(y, x, d + z);
}
}
int solve(int x, int s) {
cnt = 0;
query(x, 0, s);
sort(dis + 1, dis + cnt + 1);
int l = 1, r = cnt, sum = 0;
while (l < r) {
if (dis[l] + dis[r] <= K)
sum += r - l, ++l;
else
--r;
}
return sum;
}
void divide(int x) {
ans += solve(x, 0), vis[x] = 1;
int nowsiz = totsiz;
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i], z = edge[i];
if (vis[y]) continue;
ans -= solve(y, z);
totsiz = siz[y] > siz[x] ? nowsiz - siz[x] : siz[y];
mxsiz = INF, root = 0;
getroot(y, 0);
divide(root);
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
#endif
N = ty();
for (int i = 1; i < N; ++i) {
int x = ty(), y = ty(), z = ty();
adde(x, y, z);
adde(y, x, z);
}
K = ty();
totsiz = N, mxsiz = INF;
getroot(1, 0);
divide(root);
printf("%d\n", ans);
return 0;
}
动态点分治
好了,这才是今天的主角。
点分树
建立点分树只需要在点分治的基础上,加一行代码fa[root]=x
void divide(int x) {
vis[x] = 1;
int nowsiz = totsiz;
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i], z = edge[i];
if (vis[y]) continue;
mxsiz = INF, root = 0;
totsiz = siz[y] > siz[x] ? nowsiz - siz[x] : siz[y];
getroot(y, 0);
fa[root] = x;
divide(root);
}
}
实现修改
修改的时候不断暴跳点分树即可。
是的,你没看错,这就是动态点分治
下面根据例题来具体解读
[ZJOI2007]捉迷藏
求出点分树,对于每个结点\(x\)维护两个 可删堆 。 \(dist_x\)存储结点\(x\)代表的连通块中的所有黑点到\(fa_x\)的距离信息, \(sub_x\)表示结点\(x\)在点分树上的所有儿子和它自己中的黑点到\(x\)的距离信息,由于本题贪心的求答案方法,且两个来自于同一子树的路径不能成为一条完成的路径,我们只在这个堆中插入其自己的值和其每个子树中的最大值。我们发现,\(sub_x\)中最大的两个值的和就是分治时分支中心为\(x\)时经过结点\(x\)的最长黑端点路径。我们可以用全局可删堆\(ans\)存储所有结点的答案,这个堆中的最大值就是我们所求的答案。
#include <bits/stdc++.h>
using namespace std;
namespace IO {
const int maxn((1 << 21) + 1);
char ibuf[maxn], *iS, *iT, obuf[maxn], *oS = obuf, *oT = obuf + maxn - 1, ch,
st[55];
int opt, tp;
char Getc() {
return (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, maxn, stdin),
(iS == iT ? EOF : *iS++))
: *iS++);
}
void Flush() {
fwrite(obuf, 1, oS - obuf, stdout);
oS = obuf;
}
void Putc(char x) {
*oS++ = x;
if (oS == oT) Flush();
}
template <class Int>
void Input(Int &x) {
for (opt = 1, ch = Getc(); ch < '0' || ch > '9'; ch = Getc())
opt = ch == '-' ? -1 : 1;
for (x = 0; ch <= '9' && ch >= '0'; ch = Getc())
x = (x << 3) + (x << 1) + (ch ^ 48);
x *= opt;
}
template <class Int>
void Print(Int x) {
if (!x) Putc('0');
if (x < 0) Putc('-'), x = -x;
while (x) st[++tp] = x % 10 + '0', x /= 10;
while (tp) Putc(st[tp--]);
}
void Getstr(char *s) {
for (ch = Getc(); ch < 'A' || ch > 'Z'; ch = Getc())
;
for (; ch <= 'Z' && ch >= 'A'; ch = Getc()) *s++ = ch;
*s = 0;
}
void Putstr(const char *s) {
for (int i = 0, n = strlen(s); i < n; ++i) Putc(s[i]);
}
} // namespace IO
using IO::Flush;
using IO::Getstr;
using IO::Input;
using IO::Print;
using IO::Putc;
const int _ = 1e5 + 10;
const int INF = 0x3f3f3f3f;
int tot, head[_], to[_ << 1], nxt[_ << 1];
void adde(int x, int y) {
to[++tot] = y;
nxt[tot] = head[x];
head[x] = tot;
}
struct Heap {
priority_queue<int> data, del;
void insert(int x) { data.push(x); }
void erase(int x) { del.push(x); }
void pop() {
while (del.size() && data.top() == del.top()) data.pop(), del.pop();
data.pop();
}
int top() {
while (del.size() && data.top() == del.top()) data.pop(), del.pop();
return data.top();
}
int top2() {
int t = top(), ret;
pop();
ret = top();
data.push(t);
return ret;
}
int size() { return data.size() - del.size(); }
} dist[_], sub[_], ans;
int N, T;
int siz[_], vis[_], fa[_], dep[_], mxsiz, totsiz, root;
void getroot(int x, int f) {
siz[x] = 1;
int maxx = 0;
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (vis[y] || y == f) continue;
getroot(y, x);
siz[x] += siz[y];
maxx = max(maxx, siz[y]);
}
maxx = max(maxx, totsiz - siz[x]);
if (maxx < mxsiz) mxsiz = maxx, root = x;
}
void dfs(int x, int fa, int d, Heap &s) {
s.insert(d);
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (vis[y] || y == fa) continue;
dfs(y, x, d + 1, s);
}
}
void divide(int x) {
vis[x] = 1;
int nowsiz = totsiz;
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (vis[y]) continue;
mxsiz = INF, totsiz = siz[y] > siz[x] ? nowsiz - siz[x] : siz[y];
getroot(y, 0);
fa[root] = x;
dfs(y, x, 1, dist[root]);
sub[x].insert(dist[root].top());
dep[root] = dep[x] + 1;
divide(root);
}
sub[x].insert(0);
if (sub[x].size() >= 2)
ans.insert(sub[x].top() + sub[x].top2());
else if (sub[x].size())
ans.insert(sub[x].top());
}
namespace lca {
int top[_], dep[_], ssize[_], hson[_], fa[_];
void dfs1(int u, int ff) {
fa[u] = ff;
ssize[u] = 1;
dep[u] = dep[ff] + 1;
for (int i = head[u]; i; i = nxt[i]) {
int v = to[i];
if (v == ff) continue;
dfs1(v, u);
ssize[u] += ssize[v];
if (ssize[hson[u]] < ssize[v]) hson[u] = v;
}
}
void dfs2(int u, int tp) {
top[u] = tp;
if (hson[u]) dfs2(hson[u], tp);
for (int i = head[u]; i; i = nxt[i]) {
int v = to[i];
if (v == fa[u] || v == hson[u]) continue;
dfs2(v, v);
}
}
void init() {
dfs1(1, 0);
dfs2(1, 1);
}
int query(int u, int v) {
while (top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]]) swap(u, v);
u = fa[top[u]];
}
return dep[u] < dep[v] ? u : v;
}
int dis(int u, int v) { return dep[u] + dep[v] - dep[query(u, v)] * 2; }
} // namespace lca
int light[_], d[_][20];
inline void turn_off(int x) {
if (sub[x].size() >= 2) ans.erase(sub[x].top() + sub[x].top2());
sub[x].insert(0);
if (sub[x].size() >= 2) ans.insert(sub[x].top() + sub[x].top2());
for (int i = x; fa[i]; i = fa[i]) {
if (sub[fa[i]].size() >= 2) ans.erase(sub[fa[i]].top() + sub[fa[i]].top2());
if (dist[i].size()) sub[fa[i]].erase(dist[i].top());
dist[i].insert(d[x][dep[x] - dep[fa[i]]]);
sub[fa[i]].insert(dist[i].top());
if (sub[fa[i]].size() >= 2)
ans.insert(sub[fa[i]].top() + sub[fa[i]].top2());
}
}
inline void turn_on(int x) {
if (sub[x].size() >= 2) ans.erase(sub[x].top() + sub[x].top2());
sub[x].erase(0);
if (sub[x].size() >= 2) ans.insert(sub[x].top() + sub[x].top2());
for (int i = x; fa[i]; i = fa[i]) {
if (sub[fa[i]].size() >= 2) ans.erase(sub[fa[i]].top() + sub[fa[i]].top2());
sub[fa[i]].erase(dist[i].top());
dist[i].erase(d[x][dep[x] - dep[fa[i]]]);
if (dist[i].size()) sub[fa[i]].insert(dist[i].top());
if (sub[fa[i]].size() >= 2)
ans.insert(sub[fa[i]].top() + sub[fa[i]].top2());
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("hide.in", "r", stdin);
freopen("hide.out", "w", stdout);
#endif
Input(N);
for (int i = 1; i < N; ++i) {
int x, y;
Input(x), Input(y);
adde(x, y);
adde(y, x);
}
lca::init();
mxsiz = INF, totsiz = N;
getroot(1, 0);
divide(root);
// for (int i = 1; i <= N; ++i) printf("%d %d\n", i, fa[i]);
for (int i = 1; i <= N; ++i)
for (int j = i; j; j = fa[j]) d[i][dep[i] - dep[j]] = lca::dis(i, j);
Input(T);
while (T--) {
char op[3];
Getstr(op);
if (op[0] == 'G') {
if (ans.size())
Print(ans.top()), Putc('\n');
else
Print(-1), Putc('\n');
} else {
int x;
Input(x);
if (light[x])
turn_off(x);
else
turn_on(x);
light[x] ^= 1;
}
}
return Flush(), 0;
}
BZOJ3730 震波
Description
给你一棵树,每个节点有点权,你需要执行以下两种操作:
- 查询与点\(x\)距离不超过\(k\)的所有点的点权和
- 将点\(x\)的点权修改为\(y\)
强制在线。
Solution
还是仿照上一题的思路,将点分树建出来,然后对于每个节点,建立两个树状数组\(dist_x,sub_x\),下标为距离,权值为点权和。其中,\(dist_x\)表示\(x\)的联通块内,所有点到\(x\)的距离信息;\(sub_x\)表示\(x\)的联通块内,所有点到\(x\)在点分树上的父亲的距离信息。
对于查询,不断暴跳父亲,然后容斥一下即可。
对于修改,同样也是不断暴跳父亲,然后维护\(dist_x,sub_x\)。
需要注意的是在向上跳暴力修改的过程中,要判断节点\(x\)到当前节点的距离是否已经大于\(y-len\),且不合法时不是break
而应该是continue
,就这个我一开始用线段树写没判居然能过,改成树状数组后调了我好久。。。
PS:不过这次调试过程中,我发现了又一法宝:assert
,用这东西两下子就找出来了哪里出问题了!
Code
动态开点线段树(TLE)
#include <bits/stdc++.h>
using namespace std;
bool xxxxx;
namespace IO {
const int maxn((1 << 21) + 1);
char ibuf[maxn], *iS, *iT, obuf[maxn], *oS = obuf, *oT = obuf + maxn - 1, ch,
st[55];
int opt, tp;
char Getc() {
return (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, maxn, stdin),
(iS == iT ? EOF : *iS++))
: *iS++);
}
void Flush() {
fwrite(obuf, 1, oS - obuf, stdout);
oS = obuf;
}
void Putc(char x) {
*oS++ = x;
if (oS == oT) Flush();
}
template <class Int>
void Input(Int &x) {
for (opt = 1, ch = Getc(); ch < '0' || ch > '9'; ch = Getc())
opt = ch == '-' ? -1 : 1;
for (x = 0; ch <= '9' && ch >= '0'; ch = Getc())
x = (x << 3) + (x << 1) + (ch ^ 48);
x *= opt;
}
template <class Int>
void Print(Int x) {
if (!x) Putc('0');
if (x < 0) Putc('-'), x = -x;
while (x) st[++tp] = x % 10 + '0', x /= 10;
while (tp) Putc(st[tp--]);
}
} // namespace IO
using IO::Flush;
using IO::Input;
using IO::Print;
using IO::Putc;
const int _ = 1e5 + 10;
const int __ = 5e6 + 10;
const int INF = 0x3f3f3f3f;
int tot, head[_], to[_ << 1], nxt[_ << 1];
void adde(int x, int y) {
to[++tot] = y;
nxt[tot] = head[x];
head[x] = tot;
}
int N, M, val[_];
namespace lca {
int fa[_], son[_], dep[_], siz[_], top[_];
void dfs1(int x, int f) {
siz[x] = 1;
int maxx = 0;
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (y == f) continue;
fa[y] = x, dep[y] = dep[x] + 1;
dfs1(y, x);
siz[x] += siz[y];
if (siz[y] > maxx) maxx = siz[y], son[x] = y;
}
}
void dfs2(int x, int topf) {
top[x] = topf;
if (!son[x]) return;
dfs2(son[x], topf);
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (y == fa[x] || y == son[x]) continue;
dfs2(y, y);
}
}
void init() {
dfs1(1, 0);
dfs2(1, 1);
}
int query(int x, int y) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
x = fa[top[x]];
}
return dep[x] < dep[y] ? x : y;
}
int dis(int x, int y) { return dep[x] + dep[y] - 2 * dep[query(x, y)]; }
} // namespace lca
struct SegmentTree {
int cnt, rt[_], ls[__], rs[__], sum[__];
void modify(int &p, int l, int r, int x, int v) {
if (!p) p = ++cnt;
if (l == r) {
sum[p] += v;
return;
}
int mid = (l + r) >> 1;
if (x <= mid)
modify(ls[p], l, mid, x, v);
else
modify(rs[p], mid + 1, r, x, v);
sum[p] = sum[ls[p]] + sum[rs[p]];
}
int query(int &p, int l, int r, int x, int y) {
if (!p) return 0;
if (x <= l && r <= y) return sum[p];
int mid = (l + r) >> 1;
if (y <= mid)
return query(ls[p], l, mid, x, y);
else if (x > mid)
return query(rs[p], mid + 1, r, x, y);
else
return query(ls[p], l, mid, x, mid) +
query(rs[p], mid + 1, r, mid + 1, y);
}
} dist, sub;
int mxsiz, totsiz, root;
int vis[_], siz[_], fa[_], dep[_]; //, d[_][20];
void getroot(int x, int f) {
siz[x] = 1;
int maxx = 0;
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (vis[y] || y == f) continue;
getroot(y, x);
siz[x] += siz[y];
maxx = max(maxx, siz[y]);
}
maxx = max(maxx, totsiz - siz[x]);
if (maxx < mxsiz) mxsiz = maxx, root = x;
}
// 将距离信息插入到线段树dist中
void dfsdist(int x, int f, int d, const int s) {
dist.modify(dist.rt[s], 0, N, d, val[x]);
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (vis[y] || y == f) continue;
dfsdist(y, x, d + 1, s);
}
}
// 将距离信息插入到线段树sub中
void dfssub(int x, int f, int d, const int s) {
// printf("%d %d %d %d\n", x, d, val[x], s);
sub.modify(sub.rt[s], 0, N, d, val[x]);
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (vis[y] || y == f) continue;
dfssub(y, x, d + 1, s);
}
}
// 建立点分树
void divide(int x) {
vis[x] = 1;
dfsdist(x, 0, 0, x);
int nowsiz = totsiz;
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (vis[y]) continue;
mxsiz = INF, root = 0;
totsiz = siz[y] > siz[x] ? nowsiz - siz[x] : siz[y];
getroot(y, 0);
// printf("%d %d\n", y, root);
dfssub(y, 0, 1, root);
fa[root] = x, dep[root] = dep[x] + 1;
divide(root);
}
}
bool yyyyy;
int main() {
#ifndef ONLINE_JUDGE
freopen("earthquake.in", "r", stdin);
freopen("std.out", "w", stdout);
#endif
Input(N), Input(M);
for (int i = 1; i <= N; ++i) Input(val[i]);
for (int i = 1; i < N; ++i) {
int x, y;
Input(x), Input(y);
adde(x, y);
adde(y, x);
}
lca::init();
mxsiz = INF, totsiz = N, root = 0;
getroot(1, 0);
divide(root);
// for (int i = 1; i <= N; i++)
// for (int j = i; j; j = fa[j]) d[i][dep[i] - dep[j]] = lca::dis(i, j);
int last = 0;
while (M--) {
int op, x, y;
Input(op), Input(x), Input(y);
x ^= last, y ^= last;
cout << x << " " << y << endl;
if (op == 0) {
last = dist.query(dist.rt[x], 0, N, 0, y);
for (int i = x; fa[i]; i = fa[i]) {
int len = lca::dis(x, fa[i]);
// int len = d[x][dep[x] - dep[fa[i]]];
assert(y - len >= 0);
last += dist.query(dist.rt[fa[i]], 0, N, 0, y - len);
last -= sub.query(sub.rt[i], 0, N, 0, y - len);
}
cout << last << endl;
// Print(last), Putc('\n');
} else if (op == 1) {
dist.modify(dist.rt[x], 0, N, 0, y - val[x]);
for (int i = x; fa[i]; i = fa[i]) {
int len = lca::dis(x, fa[i]);
// int len = d[x][dep[x] - dep[fa[i]]];
dist.modify(dist.rt[fa[i]], 0, N, len, y - val[x]);
sub.modify(sub.rt[i], 0, N, len, y - val[x]);
}
val[x] = y;
}
}
// cerr << (&yyyyy - &xxxxx) / 1048576.0 << "MB" << endl;
return Flush(), 0;
}
树状数组(7784 ms)
#include <bits/stdc++.h>
using namespace std;
namespace IO {
const int maxn((1 << 21) + 1);
char ibuf[maxn], *iS, *iT, obuf[maxn], *oS = obuf, *oT = obuf + maxn - 1, ch,
st[55];
int opt, tp;
char Getc() {
return (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, maxn, stdin),
(iS == iT ? EOF : *iS++))
: *iS++);
}
void Flush() {
fwrite(obuf, 1, oS - obuf, stdout);
oS = obuf;
}
void Putc(char x) {
*oS++ = x;
if (oS == oT) Flush();
}
template <class Int>
void Input(Int &x) {
for (opt = 1, ch = Getc(); ch < '0' || ch > '9'; ch = Getc())
opt = ch == '-' ? -1 : 1;
for (x = 0; ch <= '9' && ch >= '0'; ch = Getc())
x = (x << 3) + (x << 1) + (ch ^ 48);
x *= opt;
}
template <class Int>
void Print(Int x) {
if (!x) Putc('0');
if (x < 0) Putc('-'), x = -x;
while (x) st[++tp] = x % 10 + '0', x /= 10;
while (tp) Putc(st[tp--]);
}
} // namespace IO
using IO::Flush;
using IO::Input;
using IO::Print;
using IO::Putc;
const int _ = 1e5 + 10;
const int __ = 5e6 + 10;
const int INF = 0x3f3f3f3f;
int tot, head[_], to[_ << 1], nxt[_ << 1];
void adde(int x, int y) {
to[++tot] = y;
nxt[tot] = head[x];
head[x] = tot;
}
int N, M, val[_];
namespace lca {
int fa[_], son[_], dep[_], siz[_], top[_];
void dfs1(int x, int f) {
siz[x] = 1;
int maxx = 0;
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (y == f) continue;
fa[y] = x, dep[y] = dep[x] + 1;
dfs1(y, x);
siz[x] += siz[y];
if (siz[y] > maxx) maxx = siz[y], son[x] = y;
}
}
void dfs2(int x, int topf) {
top[x] = topf;
if (!son[x]) return;
dfs2(son[x], topf);
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (y == fa[x] || y == son[x]) continue;
dfs2(y, y);
}
}
void init() {
dfs1(1, 0);
dfs2(1, 1);
}
int query(int x, int y) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
x = fa[top[x]];
}
return dep[x] < dep[y] ? x : y;
}
int dis(int x, int y) { return dep[x] + dep[y] - 2 * dep[query(x, y)]; }
} // namespace lca
struct BIT {
#define lowbit(x) (x & -x)
vector<int> c;
int lim;
BIT() {
lim = 0;
c.clear();
c.push_back(0);
}
void insert(int x, int y) {
++x;
for (; x <= lim; x += lowbit(x)) c[x] += y;
}
int query(int x) {
++x;
x = min(x, lim);
int ret = 0;
for (; x; x -= lowbit(x)) ret += c[x];
return ret;
}
#undef lowbit
} dist[_], sub[_];
int mxsiz, totsiz, root;
int vis[_], siz[_], fa[_], dep[_];
void getroot(int x, int f) {
siz[x] = 1;
int maxx = 0;
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (vis[y] || y == f) continue;
getroot(y, x);
siz[x] += siz[y];
maxx = max(maxx, siz[y]);
}
maxx = max(maxx, totsiz - siz[x]);
if (maxx < mxsiz) mxsiz = maxx, root = x;
}
void dfsdist(int x, int f, int d, const int s) {
dist[s].insert(d, val[x]);
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (vis[y] || y == f) continue;
dfsdist(y, x, d + 1, s);
}
}
void dfssub(int x, int f, int d, const int s) {
sub[s].insert(d, val[x]);
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (vis[y] || y == f) continue;
dfssub(y, x, d + 1, s);
}
}
int mxdep;
void dfsmxdep(int x, int f, int d) {
mxdep = max(mxdep, d);
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (vis[y] || y == f) continue;
dfsmxdep(y, x, d + 1);
}
}
void divide(int x) {
vis[x] = 1;
mxdep = 0, dfsmxdep(x, 0, 0);
dist[x].lim = mxdep + 1;
dist[x].c.resize(mxdep + 2, 0);
dfsdist(x, 0, 0, x);
int nowsiz = totsiz;
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (vis[y]) continue;
mxsiz = INF, root = 0;
totsiz = siz[y] > siz[x] ? nowsiz - siz[x] : siz[y];
getroot(y, 0);
mxdep = 0, dfsmxdep(y, 0, 1);
sub[root].lim = mxdep + 1;
sub[root].c.resize(mxdep + 2, 0);
dfssub(y, 0, 1, root);
fa[root] = x, dep[root] = dep[x] + 1;
divide(root);
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("earthquake.in", "r", stdin);
freopen("earthquake.out", "w", stdout);
#endif
Input(N), Input(M);
for (int i = 1; i <= N; ++i) Input(val[i]);
for (int i = 1; i < N; ++i) {
int x, y;
Input(x), Input(y);
adde(x, y);
adde(y, x);
}
lca::init();
mxsiz = INF, totsiz = N, root = 0;
getroot(1, 0);
divide(root);
int last = 0;
while (M--) {
int op, x, y;
Input(op), Input(x), Input(y);
x ^= last, y ^= last;
if (op == 0) {
last = dist[x].query(y);
for (int i = x; fa[i]; i = fa[i]) {
int len = lca::dis(x, fa[i]);
if (y - len < 0) continue;
last += dist[fa[i]].query(y - len);
last -= sub[i].query(y - len);
}
Print(last), Putc('\n');
} else if (op == 1) {
dist[x].insert(0, y - val[x]);
for (int i = x; fa[i]; i = fa[i]) {
int len = lca::dis(x, fa[i]);
dist[fa[i]].insert(len, y - val[x]);
sub[i].insert(len, y - val[x]);
}
val[x] = y;
}
}
return Flush(), 0;
}
[ZJOI2015]幻想乡战略游戏
这个题有单独的题解 链接
BZOJ4372 烁烁的游戏
这题仿照震波的做法:
还是动态点分治的套路,对于每个点开两棵动态开点的线段树或是树状数组,以距离为下标,分别存储到点\(x\)距离为\(y\)的增量,以及到点\(fa[x]\)距离为\(y\)的增量(用于容斥)。
查询和修改不断在点分树上暴跳父亲即可。
最后,由于上次震波的惨痛教训,我决定写树状数组:
树状数组跑的飞快,开心!
#include <bits/stdc++.h>
using namespace std;
#ifndef ONLINE_JUDGE
bool xxx;
#endif
inline int ty() {
char ch = getchar();
int x = 0, f = 1;
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
const int _ = 1e5 + 10;
const int __ = 1e7 + 10;
const int INF = 0x3f3f3f3f;
int N, M, tot, head[_], to[_ << 1], nxt[_ << 1];
void adde(int x, int y) {
to[++tot] = y;
nxt[tot] = head[x];
head[x] = tot;
}
namespace lca {
int fa[_], son[_], dep[_], siz[_], top[_];
void dfs1(int x, int f) {
siz[x] = 1;
int maxx = 0;
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (y == f) continue;
fa[y] = x, dep[y] = dep[x] + 1;
dfs1(y, x);
siz[x] += siz[y];
if (siz[y] > maxx) maxx = siz[y], son[x] = y;
}
}
void dfs2(int x, int topf) {
top[x] = topf;
if (!son[x]) return;
dfs2(son[x], topf);
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (y == fa[x] || y == son[x]) continue;
dfs2(y, y);
}
}
void init() {
dep[1] = 1, dfs1(1, 0);
dfs2(1, 1);
}
int query(int x, int y) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
x = fa[top[x]];
}
return dep[x] < dep[y] ? x : y;
}
int dis(int x, int y) { return dep[x] + dep[y] - 2 * dep[query(x, y)]; }
} // namespace lca
struct BIT {
#define lowbit(x) (x & -x)
vector<int> tr;
int lim;
void insert(int x, int y) {
++x;
// assert(x<=lim);
x = min(x, lim);
for (; x <= lim; x += lowbit(x)) tr[x] += y;
}
int query(int x) {
++x;
x = min(x, lim);
int ret = 0;
for (; x; x -= lowbit(x)) ret += tr[x];
return ret;
}
#undef lowbit
} dist[_], sub[_];
int mxsiz, totsiz, root, vis[_], fa[_], siz[_];
void getroot(int x, int f) {
siz[x] = 1;
int maxx = 0;
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (vis[y] || y == f) continue;
getroot(y, x);
siz[x] += siz[y];
maxx = max(maxx, siz[y]);
}
maxx = max(maxx, totsiz - siz[x]);
if (maxx < mxsiz) mxsiz = maxx, root = x;
}
int mxdep;
void dfs(int x, int f, int d) {
mxdep = max(mxdep, d);
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (vis[y] || y == f) continue;
dfs(y, x, d + 1);
}
}
void divide(int x) {
vis[x] = 1;
int nowsiz = totsiz;
mxdep = 0, dfs(x, 0, 0);
dist[x].lim = mxdep + 1;
// printf("%d %d %d\n", x, mxdep, dist[x].lim);
dist[x].tr.resize(mxdep + 3);
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (vis[y]) continue;
mxsiz = INF, root = 0;
totsiz = siz[y] > siz[x] ? nowsiz - siz[x] : siz[y];
getroot(y, 0);
mxdep = 0, dfs(y, 0, 1);
sub[root].lim = mxdep + 1;
sub[root].tr.resize(mxdep + 2, 0);
fa[root] = x;
divide(root);
}
}
int query(int x) {
int ret = dist[x].query(dist[x].lim);
for (int i = x; fa[i]; i = fa[i]) {
int len = lca::dis(x, fa[i]);
ret += dist[fa[i]].query(dist[fa[i]].lim) - dist[fa[i]].query(len - 1);
ret -= sub[i].query(sub[i].lim) - sub[i].query(len - 1);
}
return ret;
}
void modify(int x, int d, int v) {
dist[x].insert(d, v);
// assert(d <= dist[x].lim);
for (int i = x; fa[i]; i = fa[i]) {
int len = lca::dis(x, fa[i]);
if (d < len) continue;
dist[fa[i]].insert(d - len, v);
// assert(d - len <= dist[fa[i]].lim);
sub[i].insert(d - len, v);
// assert(d - len <= sub[i].lim);
}
}
#ifndef ONLINE_JUDGE
bool yyy;
#endif
int main() {
#ifndef ONLINE_JUDGE
freopen("game.in", "r", stdin);
freopen("game.out", "w", stdout);
#endif
N = ty(), M = ty();
for (int i = 1; i < N; ++i) {
int x = ty(), y = ty();
adde(x, y);
adde(y, x);
}
lca::init();
mxsiz = INF, totsiz = N, root = 0;
getroot(1, 0);
divide(1);
while (M--) {
char op[5];
int x, y, z;
scanf("%s", op), x = ty();
if (op[0] == 'Q')
printf("%d\n", query(x));
else if (op[0] == 'M') {
y = ty(), z = ty();
modify(x, y, z);
}
}
#ifndef ONLINE_JUDGE
cerr << (&yyy - &xxx) / 1048576.0 << "MB" << endl;
#endif
return 0;
}
[WC2014]紫荆花之恋
事实上,我学动态点分治就是为了写这道题,链接。