树链剖分就是 一种 机智的办法, 让人在一棵树上建出一棵线段树。
有一类题, 让你在一棵树上进行一些修改和两点间询问的操作, 怎么做呢?
倍增? 修改一个点之后它的所有儿子节点都要更新,我还没有想到什么好的方法来搞。
线段树? 似乎是可以的, 但是因为一个点的后继不止一个, 所以不能想当然地建树。
那把每个点的后继连到哪里呢? 自然地想法是连到有子儿子最多的那个儿子节点, 因为这样可以多维护一些点 ,
这是就会有这两个性质:
也就是说维护每一条重链, 然后暴力轻链的复杂度就是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;
}