树链剖分

树链剖分就是 一种 机智的办法, 让人在一棵树上建出一棵线段树。

这个介绍比较经典

有一类题, 让你在一棵树上进行一些修改和两点间询问的操作, 怎么做呢?

倍增? 修改一个点之后它的所有儿子节点都要更新,我还没有想到什么好的方法来搞。

线段树? 似乎是可以的, 但是因为一个点的后继不止一个, 所以不能想当然地建树。

那把每个点的后继连到哪里呢? 自然地想法是连到有子儿子最多的那个儿子节点, 因为这样可以多维护一些点 , 

这是就会有这两个性质: 

    性质1:如果(v,u)为轻边,则siz[u] * 2< siz[v];
      性质2:从根到某一点的路径上轻链、重链的个数都不大于logn。

也就是说维护每一条重链, 然后暴力轻链的复杂度就是log n的了。

这个划分重链轻链的过程就叫做熟练剖分。

实现也很简单, 就是两次的dfs来确定能分成哪些链和链的顶点在哪里。

void dfs1(int x, int fatt){
	sz[x] = 1; fat[x] = fatt; dep[x] = dep[fatt] + 1;
	for(int i = head[x]; i != -1; i = edge[i].next)if(edge[i].to != fatt){
		dfs1(edge[i].to, x);
		sz[x] += sz[edge[i].to];
		if(sz[edge[i].to] > sz[son[x]])son[x] = edge[i].to;
	}
}
void dfs2(int x, int topp){
	top[x] = topp; ti[x] = ++ cnt; dui[cnt] = x;
	if(son[x])dfs2(son[x], topp);
	for(int i = head[x]; i != -1; i = edge[i].next)if(edge[i].to != fat[x] && edge[i].to != son[x])dfs2(edge[i].to, edge[i].to);
}

裸题, spoj375

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define MAXN 200005
#define INF 1<<31
using namespace std;
int n, ss[MAXN], tt[MAXN], c[MAXN], ti[MAXN], head[MAXN], ee, fat[MAXN], dep[MAXN], sz[MAXN], son[MAXN], top[MAXN], w[MAXN], tot;
struct Edge{
	int to, next, c;
}edge[MAXN * 4];
struct Tree{
	int l, r, maxx;
}tree[MAXN];
inline void addedge(int s, int t){edge[++ ee].to = t; edge[ee].next = head[s]; head[s] = ee;}
void dfs1(int u, int fatt){ int cnt = 0, lala = -1, szmax = 0;
	sz[u] = 1; dep[u] = dep[fatt] + 1; son[u] = 0; fat[u] = fatt;
	for(int i = head[u]; i != -1; i = edge[i].next){
		int v = edge[i].to; if(v == fatt)continue;
		dfs1(v, u);
		sz[u] += sz[v];
		if(sz[son[u]] < sz[v])son[u] = v;
	}
}
void dfs2(int u, int fatt){
	ti[u] = tot ++;
	top[u] = fatt;
	if(son[u] != 0)dfs2(son[u], top[u]);
	for(int i = head[u]; i != -1; i = edge[i].next)
		if(edge[i].to != fat[u] && edge[i].to != son[u])
			dfs2(edge[i].to, edge[i].to);
}
void build(int l, int r, int t){
	tree[t].l = l; tree[t].r = r; tree[t].maxx = -INF;
	if(l == r)return;
	int mid = l + r >> 1;
	build(l, mid, t + t); build(mid + 1, r, t + t + 1);
}
void update(int t, int p, int w){
	if(tree[t].l == tree[t].r){
		tree[t].maxx = w; return;
	}
	int mid = tree[t].l + tree[t].r >> 1;
	if(mid >= p)update(t + t, p, w);
	else update(t + t + 1, p, w);
	tree[t].maxx = max(tree[t + t].maxx, tree[t + t + 1].maxx); 
}
int query(int l, int r, int t){
	if(tree[t].r == r && tree[t].l == l)return tree[t].maxx;
	int mid = tree[t].r + tree[t].l >> 1;
	if(mid >= r)return query(l, r, t + t);
	else if(mid < l)return query(l, r, t + t + 1);
	else return max(query(l, mid, t + t), query(mid + 1, r, t + t + 1));
}
int lca(int x, int y){
	int ans = -INF;
	while(top[x] != top[y]){
		if(dep[top[x]] < dep[top[y]])swap(x, y);
		ans = max(ans, query(ti[top[x]], ti[x], 1));
		x = fat[top[x]];
	}
	if(dep[x] > dep[y])swap(x, y);
	if(x != y)ans = max(ans, query(ti[x] + 1, ti[y], 1));
	return ans;
}
int main(){
	int t; scanf("%d", &t); while(t --){
		scanf("%d", &n);
		memset(head, -1, sizeof(head));
		for(int i = 1; i < n; i ++)scanf("%d%d%d", &ss[i], &tt[i], &c[i]), addedge(ss[i], tt[i]), addedge(tt[i], ss[i]);
		dep[1] = 0; sz[0] = 0; tot = 1;
		dfs1(1, 1); dfs2(1, 1);
		build(2, n, 1);
		for(int i = 1; i < n; i ++){
			if(dep[ss[i]] < dep[tt[i]])swap(ss[i], tt[i]);
			update(1, ti[ss[i]], c[i]);
		}
		while(1){ char s[10];
			scanf("%s", s); if(s[0] == 'D')break;
			int a, b; scanf("%d%d", &a, &b);
			if(s[0] == 'Q')printf("%d\n", lca(a, b));
				else update(1, ti[ss[a]], b);
		}puts("");
	}
	return 0;
}

裸题, bzoj1036

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define MAXN 30005
#define INF 1<<31
using namespace std;
int n, q, a[MAXN], head[MAXN], fat[MAXN], ee, sz[MAXN], son[MAXN], top[MAXN], ti[MAXN], dep[MAXN], cnt, dui[MAXN];
struct Edge{
	int to, next;
}edge[MAXN * 2];
inline void addedge(int a, int b){edge[++ ee].to = b;edge[ee].next = head[a];head[a] = ee;}
void dfs1(int x, int fatt){
	sz[x] = 1; fat[x] = fatt; dep[x] = dep[fatt] + 1;
	for(int i = head[x]; i != -1; i = edge[i].next)if(edge[i].to != fatt){
		dfs1(edge[i].to, x);
		sz[x] += sz[edge[i].to];
		if(sz[edge[i].to] > sz[son[x]])son[x] = edge[i].to;
	}
}
void dfs2(int x, int topp){
	top[x] = topp; ti[x] = ++ cnt; dui[cnt] = x;
	if(son[x])dfs2(son[x], topp);
	for(int i = head[x]; i != -1; i = edge[i].next)if(edge[i].to != fat[x] && edge[i].to != son[x])dfs2(edge[i].to, edge[i].to);
}
struct Tree{
	int l, r, maxx, summ;
}tree[MAXN * 4];
inline void pushup(int t){
	tree[t].summ = tree[t + t].summ + tree[t + t + 1].summ;
	tree[t].maxx = max(tree[t + t].maxx, tree[t + t + 1].maxx);
}
void build(int t, int l, int r){
	tree[t].l = l; tree[t].r = r; tree[t].maxx = -INF; tree[t].summ = 0;
	if(l == r){tree[t].maxx = tree[t].summ = a[dui[l]]; return;}
	int mid = l + r >> 1;
	build(t + t, l, mid); build(t + t + 1, mid + 1, r);
	pushup(t);
}
void change(int t, int p, int c){
	if(tree[t].l == tree[t].r){tree[t].maxx = tree[t].summ = c; return;}
	int mid = tree[t].l + tree[t].r >> 1;
	if(p <= mid)change(t + t, p, c);
	else change(t + t + 1, p, c);
	pushup(t);
}
int askmaxx(int t, int l, int r){
	if(tree[t].l >= l && tree[t].r <= r)return tree[t].maxx;
	int mid = tree[t].l + tree[t].r >> 1, ret = -INF;
	if(mid >= l)ret = askmaxx(t + t, l, r);
	if(mid + 1 <= r)ret = max(ret, askmaxx(t + t + 1, l, r)); 
	return ret;
}
int lcamaxx(int x, int y){ int ret = -INF;
	while(top[x] != top[y]){
		if(dep[top[x]] < dep[top[y]])swap(x, y);
		ret = max(ret, askmaxx(1, ti[top[x]], ti[x]));
		x = fat[top[x]];
	}
	if(dep[x] < dep[y])swap(x,y);
	ret = max(ret, askmaxx(1, ti[y], ti[x]));
	return ret;
}
int asksumm(int t, int l, int r){
	if(tree[t].l >= l && tree[t].r <= r)return tree[t].summ;
	int mid = tree[t].l + tree[t].r >> 1, ret = 0;
	if(mid >= l)ret = asksumm(t + t, l, r);
	if(mid + 1 <= r)ret += asksumm(t + t + 1, l, r);
	return ret;
}
int lcasumm(int x, int y){ int ret = 0, cx = x, cy = y;
	while(top[x] != top[y]){
		if(dep[top[x]] < dep[top[y]])swap(x, y);
		ret += asksumm(1, ti[top[x]], ti[x]);
		x = fat[top[x]];
	}
	if(dep[x] > dep[y])swap(x, y);
	return ret + asksumm(1, ti[x], ti[y]);
}
int main(){
	scanf("%d", &n); int ss, tt, aa, bb; char s[20];
	memset(head, -1, sizeof(head));
	for(int i = 1; i < n; i ++)scanf("%d%d", &ss, &tt), addedge(ss, tt), addedge(tt, ss);
	dfs1(1, 1); dfs2(1, 1);
	for(int i = 1; i <= n; i ++)scanf("%d", &a[i]);
	build(1, 1, n);
	scanf("%d", &q); while(q --){
		scanf("%s%d%d", s, &aa, &bb);
		if(s[1] == 'H')change(1, ti[aa], bb);
		if(s[1] == 'M')printf("%d\n", lcamaxx(aa, bb));
		if(s[1] == 'S')printf("%d\n", lcasumm(aa, bb));
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值