树链剖分:
把一棵树剖分为若干条链,然后利用数据结构(树状数组,SBT,Splay,线段树等等)去维护每一条链,复杂度
为O(logn),总体复杂度O(nlog²n)
步骤:
①将树的边分成重边和轻边,令siz[u]为u节点所有子节点的个数,v是u所有儿子中siz[]值最大的,
那么边(u,v)就是重边,否则就是轻边
深搜一次就好,顺便求出每个节点的深度和每个节点的父亲是谁
数组:在第一次深搜时全部搜出
fa[u] ------ u节点的父亲(根的父亲为0)
siz[u] ------ u节点所有子节点的个数(叶子节点的siz[]值显然为0)
dep[u] ------ u节点的深度(根的深度为1)
son[u] ------ u节点的重儿子 [边(u,son[u])为重边]
②第二次深搜,将树分解成链,[重边->重链,轻边->将两条(个)重链或叶子连接在一起],
并求出每个节点所在链的顶端节点
深搜顺序:如果当前节点是重链上的节点,则随着这条重链往下搜,搜完回溯后再搜索与这个点相连的其他轻儿子
最后每个节点对应线段(链)中的编号就是搜索的dtime!
数组:在第二次搜索时全部搜出
top[u] ------ u点所在链的顶端节点
rak[u] ------ 树中u节点剖分成链后对应的新编号
id[x] ------ 链中编号x的点对应的树的节点(反rak[])
经过前两次操作(两次搜索),树的剖分就完成了,树的问题就可以转化成链的问题
如上图:红色的边是重边,蓝色的边是轻边,红色的数字对应着当前节点在链中的编号,红点表示每个链的链头
性质:轻边(u,v)中,size(v)<=size(u/2)
从根到某一点的路径上,不超过logn条轻边和不超过logn条重边
对于每条重链,从链头到链尾编号从小到大
③:附录,例如这道题,如何修改某个节点u的值呢?
直接线段树单点更新,更新rak[u]点的值即可
如果查询两个节点u和v之间所有节点的和?
如果u和v在同一条重链中,那么好办,直接线段树区间查询一次即可,如果不在一条链中
就要让u点去找v点,不停地执行以下操作,让u点和v在同一条重链上,之后执行上个步骤
①u点爬到它所在重链的顶端节点,求出爬过的这段链的所有节点权值之和
②从该顶端节点爬向它的父亲(跑到另一条重链上了),之和继续执行步骤①
注意,如果每次u都是往上爬,深度是越来越低的!假设低过了v点的深度!那么u和v永远不可能碰面了,
所以每次爬的时候倒要比较下u和v的深度,让深度低的那个爬!!这也是为什么要求每个节点的深度
可能很难讲清楚,直接看代码好懂点
专门拉出来
int TreQuerys(int x, int y) /*步骤③,模拟u和v相遇的过程,不停地线段树查询走过节点的权值和*/
{
int sum, p1, p2;
p1 = top[x], p2 = top[y], sum = 0;
while(p1!=p2)
{
if(dep[p1]<dep[p2]) /*让深度低的那个往上爬*/
swap(p1, p2), swap(x, y);
sum += Querys(1, n, 1, rak[p1], rak[x]); /*普通线段树查询,下同*/
x = fa[p1], p1 = top[x]; /*我要爬了*/
}
if(dep[x]>dep[y])
swap(x, y);
sum += Querys(1, n, 1, rak[x], rak[y]);
return sum;
}
1036: [ZJOI2008]树的统计Count
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 16866 Solved: 6868
[ Submit][ Status][ Discuss]
Description
一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身
Input
输入的第一行为一个整数n,表示节点的个数。接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条边相连。接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。接下来1行,为一个整数q,表示操作的总数。接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。
对于100%的数据,保证1<=n<=30000,0<=q<=200000;中途操作中保证每个节点的权值w在-30000到30000之间。
Output
对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。
Sample Input
Sample Output
AC代码
#include<stdio.h>
#include<vector>
#include<string.h>
#include<algorithm>
using namespace std;
#define inf 2147483647;
vector<int> G[30005];
typedef struct
{
int sum;
int max;
}Tree;
Tree tre[125005];
int n, val[30005], son[30005], fa[30005], siz[30005], dep[30005];
int k, top[30005], rak[30005], id[30005];
void Sechs(int u, int p) /*步骤①*/
{
int v, i;
fa[u] = p;
dep[u] = dep[p]+1;
for(i=0;i<G[u].size();i++)
{
v = G[u][i];
if(v==p)
continue;
Sechs(v, u);
siz[u] += siz[v]+1;
if(son[u]==0 || siz[v]>siz[son[u]])
son[u] = v;
}
}
void Sechr(int u, int p) /*步骤②*/
{
int v, i;
top[u] = p;
rak[u] = ++k, id[k] = u;
if(son[u]==0)
return;
Sechr(son[u], p);
for(i=0;i<G[u].size();i++)
{
v = G[u][i];
if(v==son[u] || v==fa[u])
continue;
Sechr(v, v);
}
}
void Create(int l, int r, int x); /*四个线段树的简单操作*/
void Update(int l, int r, int x, int a, int b);
int Querys(int l, int r, int x, int a, int b);
int Queryx(int l, int r, int x, int a, int b);
int TreQuerys(int x, int y) /*步骤③,模拟u和v相遇的过程,不停地线段树查询走过节点的权值和*/
{
int sum, p1, p2;
p1 = top[x], p2 = top[y], sum = 0;
while(p1!=p2)
{
if(dep[p1]<dep[p2])
swap(p1, p2), swap(x, y);
sum += Querys(1, n, 1, rak[p1], rak[x]);
x = fa[p1], p1 = top[x];
}
if(dep[x]>dep[y])
swap(x, y);
sum += Querys(1, n, 1, rak[x], rak[y]);
return sum;
}
int TreQueryx(int x, int y)
{
int now, p1, p2;
p1 = top[x], p2 = top[y], now = -inf;
while(p1!=p2)
{
if(dep[p1]<dep[p2])
swap(p1, p2), swap(x, y);
now = max(now, Queryx(1, n, 1, rak[p1], rak[x]));
x = fa[p1], p1 = top[x];
}
if(dep[x]>dep[y])
swap(x, y);
now = max(now, Queryx(1, n, 1, rak[x], rak[y]));
return now;
}
int main(void)
{
int i, x, y, q;
char str[15];
while(scanf("%d", &n)!=EOF)
{
for(i=1;i<=n;i++)
G[i].clear();
for(i=1;i<=n-1;i++)
{
scanf("%d%d", &x, &y);
G[x].push_back(y);
G[y].push_back(x);
}
for(i=1;i<=n;i++)
scanf("%d", &val[i]);
memset(siz, 0, sizeof(siz));
memset(son, 0, sizeof(son));
k = 0;
Sechs(1, 0);
Sechr(1, 1);
for(i=1;i<=n;i++)
printf("%d ", rak[i]);
scanf("%d", &q);
Create(1, n, 1);
while(q--)
{
scanf("%s%d%d", str+1, &x, &y);
if(str[1]=='C')
Update(1, n, 1, rak[x], y);
else if(str[2]=='M')
printf("%d\n", TreQueryx(x, y));
else
printf("%d\n", TreQuerys(x, y));
}
}
return 0;
}
void Create(int l, int r, int x)
{
int m;
if(l==r)
{
tre[x].sum = tre[x].max = val[id[l]];
return;
}
m = (l+r)/2;
Create(l, m, x*2);
Create(m+1, r, x*2+1);
tre[x].sum = tre[x*2].sum+tre[x*2+1].sum;
tre[x].max = max(tre[x*2].max, tre[x*2+1].max);
}
void Update(int l, int r, int x, int a, int b)
{
int m;
if(l==r && r==a)
{
tre[x].max = tre[x].sum = b;
return;
}
m = (l+r)/2;
if(a<=m) Update(l, m, x*2, a, b);
else Update(m+1, r, x*2+1, a, b);
tre[x].sum = tre[x*2].sum+tre[x*2+1].sum;
tre[x].max = max(tre[x*2].max, tre[x*2+1].max);
}
int Querys(int l, int r, int x, int a, int b)
{
int m, sum;
if(l>=a && r<=b)
return tre[x].sum;
m = (l+r)/2;
sum = 0;
if(a<=m) sum += Querys(l, m, x*2, a, b);
if(b>=m+1) sum += Querys(m+1, r, x*2+1, a, b);
return sum;
}
int Queryx(int l, int r, int x, int a, int b)
{
int m, now;
if(l>=a && r<=b)
return tre[x].max;
m = (l+r)/2;
now = -inf;
if(a<=m) now = max(now, Queryx(l, m, x*2, a, b));
if(b>=m+1) now = max(now, Queryx(m+1, r, x*2+1, a, b));
return now;
}