树链剖分
什么是树链剖分
不知道。
重儿子:Size最大的儿子。
轻儿子:除了重儿子外。
重边:与重儿子之间的边。
轻边:与轻儿子之间的边。
重链:连续的重边。
轻链:同理。
通过轻重链的剖分,这棵树非常成功地被我们弄得面目全非。
这样我们再对两点间修改的时候修改的链是连续的,可证是log级别。
对于修改
对于两点之间路径上的轻链直接进行修改(反正不会超过log条),而对于比较长的重链如果直接修改无疑是很慢的,所以想到可以用一些乱七八糟的数据结构来优化这个修改。
我们平常的数据结构维护的都是链上的一些信息,所以考虑把树上的重链上的点弄成同一段连续区间,这个其实很容易通过dfs时优先遍历重儿子记下来即可。
当然在实际修改的过程中需要跳过连续的一条链,这就需要再进行一次dfs来做出来某个点所在链的最高点是哪一个点(因为轻链长度仅为1,所以轻链的最高点是自己,而重链则要通过dfs来求出)。
在dfs序上做一下线段树,查询的时候变成dfs序中的标号再查询。具体看代码
代码
#include <cstdio>
#include <cstring>
#include <iostream>
#define lc (o << 1)
#define rc ((o << 1) + 1)
#define mid ((l + r) >> 1)
#define INF 1000000007
using namespace std;
const int Maxn = 35000;
struct node {int to,next;}e[Maxn * 3];
int n,h[Maxn],w[Maxn],q,vis[Maxn],son[Maxn],maxx[Maxn * 4],cnt,tot;
int top[Maxn],num[Maxn],bel[Maxn],deep[Maxn],sum[Maxn * 4],fa[Maxn];
void Add(int u,int v) {e[++tot].to = v; e[tot].next = h[u]; h[u] = tot;}
void Dfs1(int u) {
son[u] = 1;vis[u] = 1;
for(int i = h[u];i;i = e[i].next) {
int v = e[i].to;
if(!vis[v]) {
deep[v] = deep[u] + 1;
fa[v] = u; Dfs1(v);
son[u] += son[v];
}
}
}
void Dfs2(int u,int c) {
int k = 0;
top[u] = c;
num[u] = ++cnt;
bel[cnt] = w[u];
// cout<<bel[cnt]<<endl;
for(int i = h[u];i;i = e[i].next) {
int v = e[i].to;
if(deep[v] > deep[u] && son[v] > son[k]) k = v;
}
if(k == 0) return;
Dfs2(k,c);
for(int i = h[u];i;i = e[i].next) {
int v = e[i].to;
if(deep[v] > deep[u] && v != k) Dfs2(v,v);
}
}
int Lca(int u,int v) {
while(top[u] != top[v]) {
deep[top[u]] < deep[top[v]] ? v = fa[top[v]] : u = fa[top[u]];
}
return deep[u] < deep[v] ? u : v;
}
void Update(int o) {
sum[o] = sum[lc] + sum[rc];
maxx[o] = max(maxx[lc] , maxx[rc]);
}
void Build(int l,int r,int o) {
if(l == r) {
sum[o] = maxx[o] = bel[l];
// cout<<bel[l]<<endl;
return;
}
Build(l,mid,lc);
Build(mid + 1,r,rc);
Update(o);
}
void Change(int l,int r,int o,int x,int c) {
if(l == r) {
sum[o] = maxx[o] = c;
return;
}
if(x <= mid) Change(l,mid,lc,x,c);
if(x > mid) Change(mid + 1,r,rc,x,c);
Update(o);
}
int QueryMax(int l,int r,int o,int a,int b) {
if(a <= l && b >= r) return maxx[o];
if(b <= mid) return QueryMax(l,mid,lc,a,b);
else if(a > mid) return QueryMax(mid + 1,r,rc,a,b);
else return max(QueryMax(l,mid,lc,a,mid) , QueryMax(mid + 1,r,rc,mid + 1,b));
}
int QuerySum(int l,int r,int o,int a,int b) {
if(a <= l && b >= r) return sum[o];
if(b <= mid) return QuerySum(l,mid,lc,a,b);
else if(a > mid) return QuerySum(mid + 1,r,rc,a,b);
else return QuerySum(l,mid,lc,a,b) + QuerySum(mid + 1,r,rc,mid + 1,b);
}
int SolveMax(int u,int v) {
int Max = -INF;
// cout<<top[u]<<' '<<top[v]<<endl;
while(top[u] != top[v]) {
if(deep[top[v]] > deep[top[u]]) {
Max = max(QueryMax(1,cnt,1,num[top[v]],num[v]) , Max);
v = fa[top[v]];
}
else {
Max = max(QueryMax(1,cnt,1,num[top[u]],num[u]) , Max);
u = fa[top[u]];
}
}
if(deep[u] > deep[v])swap(u,v);
return max(Max , QueryMax(1,cnt,1,num[u],num[v]));
}
int SolveSum(int u,int v) {
int Sum = 0;
while(top[u] != top[v]) {
if(deep[top[v]] > deep[top[u]]) {
Sum += QuerySum(1,cnt,1,num[top[v]],num[v]);
v = fa[top[v]];
}
else {
Sum += QuerySum(1,cnt,1,num[top[u]],num[u]);
u = fa[top[u]];
}
}
if(deep[u] > deep[v])swap(u,v);
return Sum + QuerySum(1,cnt,1,num[u],num[v]);
}
int main(){
// freopen("bzoj_1036.in","r",stdin);
// freopen("bzoj_1036.out","w",stdout);
scanf("%d",&n);
for(int i = 1;i < n;++i) {
int x,y;
scanf("%d%d",&x,&y);
Add(x,y);
Add(y,x);
}
Dfs1(1);
for(int i = 1;i <= n;++i)scanf("%d",&w[i]);
Dfs2(1,1);
Build(1,cnt,1);
// for(int i = 1;i <= n;++i)printf("%d ",num[i]);
// printf("\n");
// for(int i = 1;i <= n;++i)printf("%d ",QuerySum(1,cnt,1,num[i],num[i]));
// return 0;
scanf("%d",&q);
for(int i = 1;i <= q;i++) {
char s[7];int x,y;
scanf("%s",s);
scanf("%d%d",&x,&y);
if(s[0] == 'C') Change(1,n,1,num[x],y);
else if(s[0] == 'Q' && s[1] == 'M') {
// printf("!");
printf("%d\n",SolveMax(x,y));
}
else {
printf("%d\n",SolveSum(x,y));
}
}
return 0;
}