[ZJOI2008]树的统计Count bzoj1036 树链剖分

Description


  一棵树上有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本身

Solution


裸♂的树链剖分

根据定义我们把边分成轻重边,然后映射到线段树上。根据证明可以知道重边一定是相邻的,而且任意一点到根的路径上最多有 log2n 条非连续的轻重边,那么像lca一样跳一跳线段树查询一下就行了

Code


#include <stdio.h>
#define rep(i, st, ed) for (int i = st; i <= ed; i += 1)
#define erg(i, st) for (int i = ls[st]; i; i = e[i].next)
#define INF 0x3f3f3f3f
#define N 50001
#define E N * 11 + 1
struct edge{int x, y, next;}e[E];
struct treeNode{int l, r, mx, sum;}t[N * 5 + 1];
int size[N], pos[N], bl[N], ls[N], fa[N], dep[N], g[N], cnt;
inline void addEdge(int &cnt, int x, int y){
    cnt += 1; e[cnt] = (edge){x, y, ls[x]}; ls[x] = cnt;
}
inline void dfs1(int now){
    size[now] = 1;
    erg(i, now){
        if (e[i].y ^ fa[now]){
            fa[e[i].y] = now;
            dep[e[i].y] = dep[now] + 1;
            dfs1(e[i].y);
            size[now] += size[e[i].y];
        }
    }
}
inline void dfs2(int now, int up){
    pos[now] = ++ cnt;
    bl[now] = up;
    int mx = 0;
    erg(i, now){
        if (dep[e[i].y] > dep[now] && size[e[i].y] > size[mx]){
            mx = e[i].y;
        }
    }
    if (!mx){
        return;
    }
    dfs2(mx, up);
    erg(i, now){
        if (dep[e[i].y] > dep[now] && e[i].y != mx){
            dfs2(e[i].y, e[i].y);
        }
    }
}
inline int max(int x, int y){
    return x>y?x:y;
}
inline int queryMx(int now, int l, int r){
    if (t[now].l == l && t[now].r == r){
        return t[now].mx;
    }
    int mid = (t[now].l + t[now].r) >> 1;
    if (r <= mid){
        return queryMx(now << 1, l, r);
    }else if (l > mid){
        return queryMx(now << 1 | 1, l, r);
    }else{
        return max(queryMx(now << 1, l, mid), queryMx(now << 1 | 1, mid + 1, r));
    }
}
inline int querySum(int now, int l, int r){
    if (t[now].l == l && t[now].r == r){
        return t[now].sum;
    }
    int mid = (t[now].l + t[now].r) >> 1;
    if (r <= mid){
        return querySum(now << 1, l, r);
    }else if (l > mid){
        return querySum(now << 1 | 1, l, r);
    }else{
        return querySum(now << 1, l, mid) + querySum(now << 1 | 1, mid + 1, r);
    }
}
inline void modify(int now, int l, int r, int v){
    if (t[now].l == t[now].r){
        t[now].mx = t[now].sum = v;
        return;
    }
    int mid = (t[now].l + t[now].r) >> 1;
    if (r <= mid){
        modify(now << 1, l, r, v);
    }else if (l > mid){
        modify(now << 1 | 1, l, r, v);
    }else{
        modify(now << 1, l, mid, v);
        modify(now << 1 | 1, mid + 1, r, v);
    }
    t[now].mx = max(t[now << 1].mx, t[now << 1 | 1].mx);
    t[now].sum = t[now << 1].sum + t[now << 1 | 1].sum;
}
inline void swap(int &x, int &y){
    x ^= y;
    y ^= x;
    x ^= y;
}
inline int getMx(int x, int y){
    int mx = -INF;
    while (bl[x] ^ bl[y]){
        if (dep[bl[x]] < dep[bl[y]]){
            swap(x, y);
        }
        mx = max(mx, queryMx(1, pos[bl[x]], pos[x]));
        x = fa[bl[x]];
    }
    if (pos[x] > pos[y]){
        swap(x, y);
    }
    mx = max(mx, queryMx(1, pos[x], pos[y]));
    return mx;
}
inline int getSum(int x, int y){
    int sum = 0;
    while (bl[x] ^ bl[y]){
        if (dep[bl[x]] < dep[bl[y]]){
            swap(x, y);
        }
        sum += querySum(1, pos[bl[x]], pos[x]);
        x = fa[bl[x]];
    }
    if (pos[x] > pos[y]){
        swap(x, y);
    }
    sum += querySum(1, pos[x], pos[y]);
    return sum;
}
inline void build(int now, int l, int r){
    t[now] = (treeNode){l, r, 0, 0};
    if (l == r){
        return;
    }
    int mid = (l + r) >> 1;
    build(now << 1, l, mid);
    build(now << 1 | 1, mid + 1, r);
}
int main(void){
    int n;
    scanf("%d", &n);
    int edgeCnt = 0;
    rep(i, 1, n - 1){
        int x, y;
        scanf("%d%d", &x, &y);
        addEdge(edgeCnt, x, y);
        addEdge(edgeCnt, y, x);
    }
    rep(i, 1, n){
        scanf("%d", &g[i]);
    }
    cnt = 0;
    dfs1(1);
    dfs2(1, 1);
    build(1, 1, n);
    rep(i, 1, n){
        modify(1, pos[i], pos[i], g[i]);
    }
    int m;
    scanf("%d", &m);
    rep(i, 1, m){
        int x, y;
        char ch[10];
        scanf("%s%d%d", ch, &x, &y);
        if (ch[0] == 'C'){
            modify(1, pos[x], pos[x], y);
        }else if (ch[1] == 'M'){
            int prt = getMx(x, y);
            printf("%d\n", prt);
        }else if (ch[1] == 'S'){
            int prt = getSum(x, y);
            printf("%d\n", prt);
        }
    }
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值