剖分后的树有如下性质:
性质1:如果(v,u)为轻边,则siz[u] * 2< siz[v];
性质2:从根到某一点的路径上轻链、重链的个数都不大于logn。
总之两个dfs
dfs1(int x,int f) //f是x的父亲
枚举和x相邻的点的时候注意不等于f 才可以递归
要维护的东西:
dep[x] x节点深度
siz[x] 以x节点为根的子树的大小
son[x] 以x节点为根的子树中重儿子的编号
fa[x] x节点的父亲
dfs2(int x,int tp)
为x节点编号(线段树中的下标),并且求出每个点的top[x]
这个时候优先向x的重儿子也就是son[x]向下递归,之后再对x的其他儿子递归,但是tp的值变成了其他儿子本身,不再是top[x]
建线段树
注:如果题目中给的是边的权值,就变成深度较深的孩子的点权(之前对于一条边要看是否需要根据深度交换两个端点)
和普通的线段树的建树,修改,询问都一样
最关键的部分:
如何在不同的链之间移动?
记f1 =top[u],f2 = top[v]。
当f1<> f2时:不妨设dep[f1] >=dep[f2],那么就更新u到f1的父边的权值(logn),并使u = fa[f1]。
当f1 =f2时:u与v在同一条重链上,若u与v不是同一点,就更新u到v路径上的边的权值(logn),否则修改完成;
重复上述过程,直到修改完成。
inline int find(int va, int vb)
{
int f1 = top[va], f2 = top[vb], tmp = 0;
while (f1 != f2)
{
if (dep[f1] < dep[f2])
{ swap(f1, f2); swap(va, vb); }
tmp = max(tmp, maxi(1, 1, z, w[f1], w[va]));
va = fa[f1]; f1 = top[va];
}
if (va == vb) return tmp;
if (dep[va] > dep[vb]) swap(va, vb);
return max(tmp, maxi(1, 1, z, w[son[va]], w[vb])); // maxi 是线段树的query
}
一开始没有看到负数,不开心
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
#define MAXN 30010
int head[MAXN];
struct node{
int y,next;
}edge[2*MAXN];
int id[MAXN],son[MAXN],dep[MAXN],fa[MAXN],siz[MAXN],a[MAXN];
int l,x,y,n,q,tot;
char s[10];
int pre[MAXN],top[MAXN];
void add(int x,int y)
{
l++;
edge[l].y=y;
edge[l].next=head[x];
head[x]=l;
}
void dfs1(int x,int f)
{
int y;
fa[x]=f;
son[x]=0;//the heaviest son
siz[x]=1;
for (int i=head[x];i!=-1;i=edge[i].next)
if (edge[i].y!=f)
{
y=edge[i].y;
dep[y]=dep[x]+1;
dfs1(y,x);
siz[x]+=siz[y];
if (siz[son[x]]<siz[y])
son[x]=y;
}
}
void dfs2(int x,int tp)
{
int y;
top[x]=tp;
id[x]=++tot;// tot is the total number
pre[id[x]]=x;
if (son[x]) dfs2(son[x],tp);
for (int i=head[x];i!=-1;i=edge[i].next)
if (edge[i].y!=fa[x] && edge[i].y!=son[x])
{
y=edge[i].y;
dfs2(y,y);
}
}
struct point{
int l,r,sum,max;
}tr[4*MAXN];
void updata(int p)
{
tr[p].sum=tr[p<<1].sum+tr[p<<1|1].sum;
tr[p].max=max(tr[p<<1].max,tr[p<<1|1].max);
}
void build(int p,int l,int r)
{
tr[p].l=l;tr[p].r=r;
if (l==r) {tr[p].sum=tr[p].max=a[pre[l]]; return ;}// find the backforward l in a[] to build the tree
int mid=(l+r) >> 1;
build(p<<1, l, mid);build(p<<1|1,mid+1,r);
updata(p);
}
void change(int p,int x,int y)
{
if (tr[p].l==x && tr[p].r==x)
{
tr[p].sum=tr[p].max=y;
return ;
}
int mid=(tr[p].l+tr[p].r) >> 1;
if (x<=mid) change(p<<1, x, y);
if (x>mid) change(p<<1|1, x, y);
updata(p);
}
int ask_max(int p,int l,int r)
{
if (tr[p].l==l && tr[p].r==r)
return tr[p].max;
int mid=(tr[p].l+tr[p].r) >> 1;
if (r<=mid) return ask_max(p<<1, l, r);
if (l>mid) return ask_max(p<<1|1, l, r);
if (l<=mid && r>mid)
{
int s1=ask_max(p<<1, l, mid);
int s2=ask_max(p<<1|1, mid+1, r);
return max(s1,s2);
}
}
int ask_sum(int p,int l,int r)
{
if (tr[p].l==l && tr[p].r==r)
return tr[p].sum;
int mid=(tr[p].l+tr[p].r) >> 1;
if (r<=mid) return ask_sum(p<<1, l, r);
if (l>mid) return ask_sum(p<<1|1, l, r);
if (l<=mid && r>mid)
{
int s1=ask_sum(p<<1, l, mid);
int s2=ask_sum(p<<1|1, mid+1, r);
return s1+s2;
}
}
int find_max(int x, int y)
{
int f1=top[x],f2=top[y],tmp=-0x3f3f3f;//有负数
while (f1!=f2)
{
if (dep[f1]<dep[f2])
{swap(f1,f2);swap(x,y);}
tmp=max(tmp, ask_max(1,id[f1],id[x]));
x=fa[f1];f1=top[x];
}
if (x==y) return max(tmp,ask_max(1,id[x],id[x]));
if (dep[x]>dep[y]) swap(x,y);
return max(tmp, ask_max(1, id[x], id[y]));
}
int find_sum(int x,int y)
{
int f1=top[x], f2=top[y], tmp=0;
while (f1!=f2)
{
if (dep[f1]<dep[f2])
{ swap(f1,f2);swap(x,y);}
tmp+=ask_sum(1,id[f1],id[x]);
x=fa[f1];f1=top[x];
}
if (x==y) return tmp+ask_sum(1,id[x],id[y]);
if (dep[x]>dep[y]) swap(x,y);
return tmp+ask_sum(1,id[x], id[y]);
}
int main()
{
scanf("%d", &n);
memset(head,-1,sizeof(head));
for (int i=1;i<n;i++)
{
scanf("%d%d", &x, &y);
add(x,y);
add(y,x);
}
for (int i=1;i<=n;i++) scanf("%d", &a[i]);
dfs1(1,0);
dfs2(1,1);
build(1,1,n);
scanf("%d", &q);
while (q--)
{
scanf("%s%d%d", s, &x, &y);
if (s[0]=='C') change(1,id[x],y); // use id[x] !!
if (s[0]=='Q' && s[1]=='M') printf("%d\n", find_max(x,y));//x and y are two points
if (s[0]=='Q' && s[1]=='S') printf("%d\n", find_sum(x,y));//if there is X types of queries, we need to write X parts for the queries
}
}