HDU-4918 Query on the subtree(树分治+树状数组)

本文介绍了一种结合树分治和树状数组的数据结构算法,用于解决特定类型的树形结构查询问题。该算法能够高效地处理树上节点的权重更改及查询距离某一节点不超过特定值的所有节点总权重。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Query on the subtree

Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 1193    Accepted Submission(s): 375


Problem Description
bobo has a tree, whose vertices are conveniently labeled by 1,2,…,n. At the very begining, the i-th vertex is assigned with weight w i.

There are q operations. Each operations are of the following 2 types:

Change the weight of vertex v into x (denoted as "! v x"),
Ask the total weight of vertices whose distance are no more than d away from vertex v (denoted as "? v d").

Note that the distance between vertex u and v is the number of edges on the shortest path between them.
 

Input
The input consists of several tests. For each tests:

The first line contains n,q (1≤n,q≤10 5). The second line contains n integers w 1,w 2,…,w n (0≤w i≤10 4). Each of the following (n - 1) lines contain 2 integers a i,b i denoting an edge between vertices a i and b i (1≤a i,b i≤n). Each of the following q lines contain the operations (1≤v≤n,0≤x≤10 4,0≤d≤n).
 

Output
For each tests:

For each queries, a single number denotes the total weight.
 

Sample Input
  
4 3 1 1 1 1 1 2 2 3 3 4 ? 2 1 ! 1 0 ? 2 1 3 3 1 2 3 1 2 1 3 ? 1 0 ? 1 1 ? 1 2
 

Sample Output
  
3 2 1 6 6

题解:树分治+树状数组

首先看到询问任意点对的距离不超过d就想到用树分治来写,通过不断找重心将树划分,递归层数不会超过logN。如果不进行修改操作,我们就只要用数组cnt[u][dis]储存到点u距离小于dis的权值和。

但是现在需要进行修改,因此需要用数据结构来维护:对于一个点u,他最多只属于logN个子树,也就是最多只属于logN个重心。所以我们可以预处理出每个点所属于的重心(这个可以在树分治时完成)以及到这些重心的距离,以每个重心建树状数组,每个点按照到重心的距离插入到树状数组中,然后每次查询到u距离不超过d的点的个数就通过树状数组求前缀和得到。

假设子树的重心为root,对于这颗子树的一个节点v,设subroot是root的子节点且是v的根节点,那么到u距离不超过d的点的个数为:T[root].sum[d-dis]-T[subroot].sum[d-dis]

关于这道题,鸟神的博客写的超级棒:点击打开链接

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MX = 1e5 + 5;
const int MXM = 4e6 + 5;
const int INF = 0x3f3f3f3f;
struct Edge {
    int v, nxt;
} E[MX * 2];
int head[MX], rear;
int fir[MX], tot;
int sz[MX], maxv[MX], dep[MX],  id[MX];
int val[MX], vis[MX], n, cnt, Max, root;
void init() {
    memset(vis, 0, sizeof(vis));
    memset(head, -1, sizeof(head));
    memset(fir, -1, sizeof(fir));
    rear = cnt = tot = 0;
}
void add(int u, int v) {
    E[rear].v = v;
    E[rear].nxt = head[u];
    head[u] = rear++;
}
struct Tree {
    int n ;
    vector < int > T ;
    void init (int sz) {
        T.clear();
        n = sz;
        T.resize(n + 1);
    }
    void add (int x, int v) {
        for (int i = x; i <= n; i += i & -i) T[i] += v;
    }
    int sum (int x) {
        if (x > n) x = n;
        int ret = 0;
        for (int i = x; i > 0; i -= i & -i) ret += T[i];
        return ret;
    }
} T[MX << 1];
//类似于领接表,node记录包含u的所有根节点以及根节点的子树
struct node {
    int root, subroot; //根节点,根节点的子节点
    int dis, nxt;
} Node[MXM];
void add_node(int u, int root, int subroot, int dis) {
    Node[tot].root = root;
    Node[tot].subroot = subroot;
    Node[tot].dis = dis;
    Node[tot].nxt = fir[u];
    fir[u] = tot++;
}
void dfs_size(int u, int fa) {
    sz[u] = 1; maxv[u] = 0;
    for (int i = head[u]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        if (vis[v] || v == fa) continue;
        dfs_size(v, u);
        sz[u] += sz[v];
        maxv[u] = max(maxv[u], sz[v]);
    }
}
void dfs_root(int u, int fa, int rt) {
    maxv[u] = max(maxv[u], sz[rt] - sz[u]);
    if (maxv[u] < Max) {
        Max = maxv[u];
        root = u;
    }
    for (int i = head[u]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        if (vis[v] || v == fa) continue;
        dfs_root(v, u, rt);
    }
}
void dfs_deep(int u, int fa, int deep, int root, int subroot) {
    dep[u] = deep;
    T[root].add(dep[u], val[u]);
    T[subroot].add(dep[u], val[u]);
    add_node(u, root, subroot, dep[u]);
    for (int i = head[u]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        if (vis[v] || v == fa) continue;
        dfs_deep(v, u, deep + 1, root, subroot);
    }
}
void DFS(int u) {
    Max = n;
    dfs_size(u, -1); dfs_root(u, -1, u);
    int rt = root; id[rt] = ++cnt;
    vis[rt] = 1;
    dep[rt] = 1;  //设根节点深度为1
    T[cnt].init(sz[u] + 1);
    T[cnt].add(1, val[rt]);
    add_node(rt, id[rt], 0, 1);
    for (int i = head[rt]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        if (vis[v]) continue;
        T[++cnt].init(sz[v] + 1);
        dfs_deep(v, rt, 2, id[rt], cnt);
    }
    for (int i = head[rt]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        if (vis[v]) continue;
        DFS(v);
    }
}
int main() {
    int m;
    //freopen("in.txt","r",stdin);
    while (~scanf("%d%d", &n, &m)) {
        init();
        for (int i = 1; i <= n; i++) scanf("%d", &val[i]);
        for (int i = 1, u, v; i < n; i++) {
            scanf("%d%d", &u, &v);
            add(u, v); add(v, u);
        }
        DFS(1);
        char op[5]; int u, x;
        while (m--) {
            scanf("%s%d%d", op, &u, &x);
            if (op[0] == '!') {
                for (int i = fir[u]; ~i; i = Node[i].nxt) {
                    int root = Node[i].root, subroot = Node[i].subroot, dis = Node[i].dis;
                    T[root].add(dis, x - val[u]);
                    if (subroot) T[subroot].add(dis, x - val[u]);
                }
                val[u] = x;
            } else {
                int ans = 0;
                for (int i = fir[u]; ~i; i = Node[i].nxt) {
                    int root = Node[i].root, subroot = Node[i].subroot, dis = Node[i].dis - 1;
                    ans += T[root].sum(x - dis + 1);
                    if (subroot) ans -= T[subroot].sum(x - dis + 1);
                }
                printf("%d\n", ans);
            }
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值