题目大意:有一棵n个点的树,现要询问某一区间的和或最小值,并改变某一点的权值
思路:(树链剖分)将树上的点以dfs序建立线段树,之后进行线段树的操作,操作的左端点和右端点为dfs序的点的编号
#include<__msvc_all_public_headers.hpp>
using namespace std;
const int N = 3e4 + 5;
struct Edge
{
int v, next;
}edge[N << 1];//链式前向星存图
int head[N], a[N], dfn[N];
int cnt = 0;
struct Tree
{
int l, r, sum, max;//线段树
}tree[N << 2];
void addedge(int u, int v)
{
edge[++cnt].v = v;
edge[cnt].next = head[u];
head[u] = cnt;
}
int son[N], siz[N], dep[N], fa[N];
void dfs1(int u)
{
son[u] = -1;//重子节点
siz[u] = 1;//子节点大小
for (int i = head[u]; i; i = edge[i].next)
{
int v = edge[i].v;
if (dep[v])//防止重复访问
continue;
dep[v] = dep[u] + 1;//当前深度
fa[v] = u;//父结点信息
dfs1(v);
siz[u] += siz[v];
if (son[u] == -1 || siz[v] > siz[son[u]])//取子节点中子树最大的点为重节点
son[u] = v;
}
}
int top[N], tot = 0, rnk[N];
void dfs2(int u, int t)
{
top[u] = t;//所在链的链顶
dfn[u] = ++tot;//dfs序
rnk[tot] = u;//dfs序编号
if (son[u] == -1)
return;
dfs2(son[u], t);
for (int i = head[u]; i; i = edge[i].next)
{
int v = edge[i].v;
if (dfn[v])
continue;
dfs2(v, v);
}
}
void cal(int rt)
{//将子节点信息传给父结点
tree[rt].sum = tree[rt << 1].sum + tree[rt << 1 | 1].sum;
tree[rt].max = max(tree[rt << 1].max, tree[rt << 1 | 1].max);
}
void build(int l, int r, int rt)
{//建立线段树
tree[rt].l = l;
tree[rt].r = r;
if (l == r)
{
tree[rt].max = tree[rt].sum = a[rnk[l]];
return;
}
int mid = (l + r) >> 1;
build(l, mid, rt << 1);
build(mid + 1, r, rt << 1 | 1);
cal(rt);
}
void change(int pos, int d, int num)
{//单点修改
if (tree[num].l == tree[num].r)
{//找到了最底层的节点
tree[num].max = tree[num].sum = d;
return;
}
int mid = (tree[num].l + tree[num].r >> 1);
if (pos <= mid)
{
change(pos, d, num << 1);
}
else
{
change(pos, d, num << 1 | 1);
}
cal(num);
}
int calsum(int l, int r, int num)
{//线段树求和
if (l <= tree[num].l && r >= tree[num].r)
{
return tree[num].sum;
}
int mid = (tree[num].l + tree[num].r) >> 1;
int val = 0;
if (l <= mid)
{
val += calsum(l, r, num << 1);
}
if (r > mid)
{
val += calsum(l, r, num << 1 | 1);
}
return val;
}
int calmax(int l, int r, int num)
{//线段树查询最值
if (l <= tree[num].l && r >= tree[num].r)
{
return tree[num].max;
}
int mid = (tree[num].l + tree[num].r) >> 1;
int val = -(1 << 30);
if (l <= mid)
{
val = max(val, calmax(l, r, num << 1));
}
if (r > mid)
{
val = max(val, calmax(l, r, num << 1 | 1));
}
return val;
}
int querysum(int x, int y)
{
int ret = 0, fx = top[x], fy = top[y];
while (fx != fy)
{
if (dep[fx] >= dep[fy])
{
ret += calsum(dfn[fx], dfn[x], 1);
x = fa[fx];
fx = top[x];
}
else
{
ret += calsum(dfn[fy], dfn[y], 1);
y = fa[fy];
fy = top[y];
}
}
if (dfn[x] < dfn[y])
{
ret += calsum(dfn[x], dfn[y], 1);
}
else
{
ret += calsum(dfn[y], dfn[x], 1);
}
return ret;
}
int querymax(int x, int y)
{
int ret = -(1 << 30), fx = top[x], fy = top[y];
while (fx != fy)//直到链的端点相遇
{
if (dep[fx] >= dep[fy])//确保区间左端点小于右端点
{
ret = max(ret, calmax(dfn[fx], dfn[x], 1));
x = fa[fx];
fx = top[x];//将链的顶点向上移,维护线段树
}
else
{
ret = max(ret, calmax(dfn[fy], dfn[y], 1));
y = fa[fy];
fy = top[y];
}
}
if (dfn[x] < dfn[y])//确保区间左端点小于右端点
{
ret = max(ret, calmax(dfn[x], dfn[y], 1));
}
else
{
ret = max(ret, calmax(dfn[y], dfn[x], 1));
}
return ret;
}
int main()
{
int n;
cin >> n;
for (int i = 1; i < n; i++)
{
int u, v;
scanf_s("%d%d", &u, &v);
addedge(u, v);
addedge(v, u);
}
for (int i = 1; i <= n; i++)
{
scanf_s("%d", &a[i]);
}
dep[1] = 1;
dfs1(1);
dfs2(1, 1);
build(1, n, 1);
int q;
cin >> q;
while (q--)
{
char op[10];
int l, r;
cin >> op;
scanf_s("%d%d", &l, &r);
if (op[0] == 'C')
{
change(dfn[l], r, 1);
}
else if (op[1] == 'M')
{
printf("%d\n", querymax(l, r));
}
else
{
printf("%d\n", querysum(l, r));
}
}
return 0;
}