HYSBZ - 1036 树的统计Count (树链剖分)

树链剖分就是将一个树的路径转化成重链和轻链,对其节点或者边就行编号,从而可以转化成其他的数据结构问题来解决问题。树链剖分有些类似莫队,也就是相当于把树的路径进行了分块。

数组的含义:

fa[]每个节点的父节点
num[]每个节点子节点的个数(包含自己)
son[v]与v在同一重链的重儿子
top[v]节点v所在链的顶端节点(v可以是一个单独的点,相当于自己组成一条重链)
pos[v]节点v编号后在线段树中的编号
pre[v]v节点之前的编号
deep[v]节点v在树中的深度(根深度为1)

修改或者查询时:

对于[u,v]区间,f1=top[u],f2=top[v],ql,qr分别为左右端点
有两种情况(deep[f1]>deep[f2]):
f1!=f2: ql=pos[top[u]],qr=pos[u],再更新u=fa[top[u]]
f1==f2: ql=pos[u],qr=pos[v] (deep[u] < deep[v])
重复循环上面的过程,ql,qr就是需要查询和修改的左右区间

【HYSBZ - 1036 树的统计】

这个题是裸的树剖加线段树
  一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成
一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I
II. 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
4
1 2
2 3
4 1
4 2 1 3
12
QMAX 3 4
QMAX 3 3
QMAX 3 2
QMAX 2 3
QSUM 3 4
QSUM 2 1
CHANGE 1 5
QMAX 3 4
CHANGE 3 6
QMAX 3 4
QMAX 2 4
QSUM 3 4
Sample Output
4
1
2
2
10
6
5
6
5
16
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
const int maxn = 1e5 + 5;
struct node {
    int l, r, maxx, sum;
}tree[maxn<<2];
int pos[maxn], pre[maxn], value[maxn], son[maxn], num[maxn], top[maxn], deep[maxn],fa[maxn];
int n, tot, q;
vector<int>e[maxn];

void dfs1(int u,int f,int dep)
{
    deep[u] = dep;
    num[u] = 1;
    fa[u] = f;
    for (int i = 0; i < e[u].size(); i++) {
        int v = e[u][i];
        if (v == fa[u]) continue;
        dfs1(v, u, dep + 1);
        num[u] += num[v];
        if (num[v] > num[son[u]])
            son[u] = v;
    }
}
void dfs2(int u,int tp)
{
    pos[u] = ++tot; pre[pos[u]] = u;
    top[u] = tp;
    if (son[u]) dfs2(son[u], tp);
    for (int i = 0; i < e[u].size(); i++) {
        int v = e[u][i];
        if (v == fa[u] || v == son[u]) continue;
        dfs2(v, v);
    }
}

inline void pushup(int rt)
{
    tree[rt].maxx = max(tree[rt << 1].maxx, tree[rt << 1 | 1].maxx);
    tree[rt].sum = tree[rt << 1].sum + tree[rt << 1 | 1].sum;
}
void build(int l, int r, int rt)
{
    tree[rt].l = l; tree[rt].r = r;
    if (l == r) {
        tree[rt].maxx = tree[rt].sum = value[pre[l]];
        return;
    }
    int mid = l + r >> 1;
    build(l, mid, rt << 1);
    build(mid + 1, r, rt << 1 | 1);
    pushup(rt);
}
void update(int pos, int rt, int val)
{
    if (tree[rt].l == tree[rt].r) {
        tree[rt].sum = tree[rt].maxx = val;
        return;
    }
    int mid = tree[rt].l + tree[rt].r >> 1;
    if (pos <= mid) update(pos, rt << 1, val);
    else if (pos > mid) update(pos, rt << 1 | 1, val);
    pushup(rt);
}
pair<int, int> query(int l, int r, int rt)
{
    pair<int, int>ret;
    if (l == tree[rt].l&&r == tree[rt].r) {
        ret.first = tree[rt].maxx;
        ret.second = tree[rt].sum;
        return ret;
    }
    int mid = tree[rt].l + tree[rt].r >> 1;
    if (r <= mid) return query(l, r, rt << 1);
    else if (l > mid) return query(l, r, rt << 1 | 1);
    else {
        pair<int, int>a, b;
        a = query(l, mid, rt << 1);
        b = query(mid + 1, r, rt << 1 | 1);
        ret.first = max(a.first, b.first);
        ret.second = a.second + b.second;
        return ret;
    }
    pushup(rt);
}
int getsum(int u, int v)
{
    int f1 = top[u], f2 = top[v], ret = 0;
    while (f1 != f2) {
        if (deep[f1] < deep[f2]) {
            swap(f1, f2); swap(u, v);
        }
        ret += query(pos[top[u]], pos[u], 1).second;
        u = fa[f1]; f1 = top[u];
    }
    if (deep[u] < deep[v]) swap(u, v);
    ret += query(pos[v], pos[u], 1).second;
    return ret;
}
int getmax(int u, int v)
{
    int f1 = top[u], f2 = top[v], ret = -0x3f3f3f3f;
    while (f1 != f2) {
        if (deep[f1] < deep[f2]) {
            swap(f1, f2); swap(u, v);
        }
        ret =max(ret, query(pos[top[u]], pos[u], 1).first);
        u = fa[f1]; f1 = top[u];
    }
    if (deep[u] < deep[v]) swap(u, v);
    ret = max(ret, query(pos[v], pos[u], 1).first);
    return ret;
}
int main()
{
    scanf("%d",&n);
    for (int i = 1; i < n; i++) {
        int a, b;
        scanf("%d%d",&a,&b);
        e[a].push_back(b);
        e[b].push_back(a);
    }
    for (int i = 1; i <= n; i++) scanf("%d",&value[i]);
    dfs1(1, 1, 1);
    dfs2(1, 1);
    build(1, tot, 1);
    scanf("%d",&q);
    for (int i=1 ; i <= q; i++) {
        char op[10];
        int u, v;
        scanf("%s%d%d",op,&u,&v);
        if (strcmp(op, "QSUM") == 0) {
            printf("%d\n",getsum(u,v));
        }
        else if (strcmp(op, "QMAX") == 0) {
            printf("%d\n", getmax(u, v));
        }
        else {
            update(pos[u], 1, v);
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值