CSAcademy Round 10 Yury's Tree(有根树点分树或kruskal重构树)

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
#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 e[N];
vector 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 &f, int x, ll y) {
	for(; x < f.si; x += low(x)) f[x] += y;
}
ll sum(vector &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重构树))