https://csacademy.com/contest/archive/task/yurys-tree
https://csacademy.com/submission/2761667/
kruskal重构树的\(log^2\)做法非常显然,这里就不讲了。
这里讲讲有根树的点分治做法。
依旧是分治,对一个分治子树,考虑所有经过分治重心的路径。
设这个分治子树中在原树上深度最小的点为它的根\(root\),分治重心为\(x\)。
由于必须是上到下的路径,因为要经过\(x\),所以路径的起点一定是\(root\)到\(x\)的路径上的点,而终点就是除\(root\)所在\(x\)的子树外的所有点(\(x\)也行)。
处理一下每个点到\(x\)的边权最小值,再把可以作为终点的点以这个排序,每次修改时就二分一个后缀,然后树状数组加即可。
时间复杂度:\(O(n~log^2~n)\)。
有根树点分治可以做更多的事情,做这道题有点杀鸡焉用牛刀。
Code:
#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i < _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;
const int N = 1e5 + 5;
int n, Q;
int x, y, z, op;
int a[N];
int fi[N], to[N * 2], nt[N * 2], w[N * 2], tot;
void link(int x, int y, int z) {
nt[++ tot] = fi[x], to[tot] = y, w[tot] = z, fi[x] = tot;
}
void Init() {
scanf("%d %d", &n, &Q);
fo(i, 1, n) scanf("%d", &a[i]);
fo(i, 1, n - 1) {
scanf("%d %d %d", &x, &y, &z);
link(x, y, z); link(y, x, z);
}
}
int dep[N];
namespace sub1 {
int bz[N];
void dg(int x) {
bz[x] = 1;
for(int i = fi[x]; i; i = nt[i]) {
int y = to[i]; if(bz[y]) continue;
dep[y] = dep[x] + 1;
dg(y);
}
}
}
int siz[N], mx[N], G, bz[N];
void fg(int x) {
bz[x] = 1;
siz[x] = 1; mx[x] = 0;
for(int i = fi[x]; i; i = nt[i]) {
int y = to[i]; if(bz[y]) continue;
fg(y);
siz[x] += siz[y];
mx[x] = max(mx[x], siz[y]);
}
mx[x] = max(mx[x], siz[0] - siz[x]);
if(mx[x] < mx[G]) G = x;
bz[x] = 0;
}
int D, FR;
int mi[18][N], fr[18][N], id[18][N];
int d[N], d0;
int fa[N];
int cd[N];
void dfs(int x, int s, int la) {
fa[x] = la;
bz[x] = 1;
mi[D][x] = s;
fr[D][x] = FR;
d[++ d0] = x;
for(int i = fi[x]; i; i = nt[i]) {
int y = to[i]; if(bz[y]) continue;
dfs(y, min(s, w[i]), x);
}
bz[x] = 0;
}
int ok[18][N];
int cmpd(int x, int y) {
return mi[D][x] < mi[D][y];
}
vector<int> e[N];
vector<ll> f[N];
#define pb push_back
#define si size()
int ie[18][N];
int fq[N];
void dg(int de, int x) {
fg(x);
cd[x] = de;
D = de;
mi[D][x] = 1e9;
d[d0 = 1] = x;
fa[x] = 0;
bz[x] = 1;
for(int i = fi[x]; i; i = nt[i]) {
int y = to[i]; if(bz[y]) continue;
FR = y;
dfs(y, w[i], x);
}
int rt = x;
fo(i, 1, d0) if(dep[d[i]] < dep[rt])
rt = d[i];
fo(i, 1, d0) id[D][d[i]] = x;
for(int p = rt; p; p = fa[p]) {
ok[D][p] = 1;
}
for(int i = 1; i <= d0; i ++) if(d[i] != x && fr[D][d[i]] == fr[D][rt]) {
swap(d[i], d[d0]); d0 --; i --;
}
sort(d + 1, d + d0 + 1, cmpd);
e[x].pb(0);
fo(i, 1, d0) {
e[x].pb(d[i]);
ie[D][d[i]] = i;
}
f[x].resize(e[x].si);
bz[x] = 1;
for(int i = fi[x]; i; i = nt[i]) {
int y = to[i]; if(bz[y]) continue;
siz[0] = siz[y], G = 0, fg(y);
fq[G] = x;
dg(de + 1, G);
}
}
void build() {
dep[1] = 1;
sub1 :: dg(1);
siz[0] = mx[0] = n;
G = 0, fg(1), dg(0, G);
}
#define low(x) ((x) & -(x))
void add(vector<ll> &f, int x, ll y) {
for(; x < f.si; x += low(x)) f[x] += y;
}
ll sum(vector<ll> &f, int x) {
ll s = 0;
for(; x; x -= low(x)) s += f[x];
return s;
}
ll qry(int x) {
ll ans = 0;
for(int p = x; p; p = fq[p]) {
int D = cd[p];
if(ie[D][x]) {
ans += sum(f[p], ie[D][x]);
}
}
return ans;
}
void xiu(int x, int y, int z) {
for(int p = x; p; p = fq[p]) {
int D = cd[p];
if(ok[D][x] && mi[D][x] >= y) {
int as = -1;
for(int l = 1, r = e[p].si - 1; l <= r; ) {
int m = l + r >> 1;
if(mi[D][e[p][m]] >= y) {
as = m, r = m - 1;
} else l = m + 1;
}
if(as != -1) {
add(f[p], as, z);
}
}
}
}
void End() {
fo(ii, 1, Q) {
scanf("%d", &op);
if(op == 1) {
scanf("%d", &x);
pp("%lld\n", qry(x) + a[x]);
} else {
scanf("%d %d %d", &z, &y, &x);
xiu(x, y, z);
}
}
}
int main() {
Init();
build();
End();
}
CSAcademy Round 10 Yury's Tree(有根树点分树或kruskal重构树)
原文:https://www.cnblogs.com/coldchair/p/13423705.html