HDU 5044 Tree 树链剖分

本文介绍了一道关于树的区间更新问题,利用树链剖分技术进行优化。通过对比线段树懒惰传播的方法,展示了类似树状数组的高效区间更新方式,并分享了实现代码。

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

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5044


题意:给定一个树,点和边都有权值,初始都为0。有t组数据,每组数据输入n和m,代表节点数和操作数,接下来n - 1行,每行两个数,代表两点有边相连。然后m行,形式如

ADD1 a b c 或 ADD2 a b c,前一种是把点a到点b路径上的点权都加上c,后一种是把点a到点b路径上的边权都加上c。最后输出每个点的点权和每条边的边权(边权按输入边的顺序输出)


思路:树链剖分,同时维护边权和点权。刚开始用的线段树lazy操作,超时了。。。然后百度一发,区间更新时用类似树状数组的方式,更为高效(并不知道这种方式具体叫什么,只是很常用就是了。。。),中间很无语的PE一发,就过了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 100010;
struct edge
{
    int to, next;
}g[N*2];
struct node
{
    int l, r, nval, eval, nmark, emark;
}s[N*4];
int head[N], siz[N], son[N], top[N], fat[N], dep[N], id[N];
int d[N][2], ecnt[N], ncnt[N];
int n, m, cnt, num, _case = 0;
void add_edge(int v, int u)
{
    g[cnt].to = u;
    g[cnt].next = head[v];
    head[v] = cnt++;
}
void dfs1(int v, int d, int fa)
{
    siz[v] = 1, son[v] = 0, fat[v] = fa, dep[v] = d;
    for(int i = head[v]; i != -1; i = g[i].next)
    {
        int u = g[i].to;
        if(u != fa)
        {
            dfs1(u, d + 1, v);
            siz[v] += siz[u];
            if(siz[son[v]] < siz[u]) son[v] = u;
        }
    }
}
void dfs2(int v, int tp)
{
    top[v] = tp, id[v] = ++num;
    if(son[v]) dfs2(son[v], tp);
    for(int i = head[v]; i != -1; i = g[i].next)
    {
        int u = g[i].to;
        if(u != fat[v] && u != son[v]) dfs2(u, u);
    }
}
void update(int l, int r, int c, int q)
{
    /*至今不知道这种姿势叫什么,虽然用过很多次*/
    if(q == 1) ncnt[l] += c, ncnt[r+1] -= c;
    else ecnt[l] += c, ecnt[r+1] -= c;
}
void renew(int v, int u, int c, int q)
{
    int t1 = top[v], t2 = top[u];
    while(t1 != t2)
    {
        if(dep[t1] < dep[t2])
            swap(t1, t2), swap(v, u);
        update(id[t1], id[v], c, q);
        v = fat[t1], t1 = top[v];
    }
    if(dep[v] > dep[u]) swap(v, u);
    if(q == 1) update(id[v], id[u], c, q);
    else
    {
        if(u != v) update(id[son[v]], id[u], c, q);
    }
}
void slove()
{
    char str[10];
    int a, b, c;
    memset(ncnt, 0, sizeof ncnt);
    memset(ecnt, 0, sizeof ecnt);
    while(m--)
    {
        scanf(" %s%d%d%d", str, &a, &b, &c);
        if(strcmp(str, "ADD1") == 0)
            renew(a, b, c, 1);
        else renew(a, b, c, 2);
    }
    printf("Case #%d:\n", ++_case);
    for(int i = 1; i <= n; i++)
        ncnt[i] += ncnt[i-1], ecnt[i] += ecnt[i-1];
    for(int i = 1; i <= n; i++)
        if(i != n) printf("%d ", ncnt[id[i]]);
        else printf("%d\n", ncnt[id[i]]);
    for(int i = 1; i <= n - 1; i++)
        if(i != n - 1) printf("%d ", ecnt[id[d[i][1]]]);
        else printf("%d\n", ecnt[id[d[i][1]]]);
    if(n == 1) printf("\n"); /*点为1时没有边,但要输出一个空行,PE了一次。。。无语*/
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        memset(head, -1, sizeof head);
        cnt = num = 0;
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n -1; i++)
        {
            scanf("%d%d", &d[i][0], &d[i][1]);
            add_edge(d[i][0], d[i][1]);
            add_edge(d[i][1], d[i][0]);
        }
        dfs1(1, 1, 0);
        dfs2(1, 1);
        for(int i = 1; i <= n - 1; i++)
            if(dep[d[i][0]] > dep[d[i][1]]) swap(d[i][0], d[i][1]);
        slove();
    }
    return 0;
}

附线段树超时代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 100010;
struct edge
{
    int to, next;
}g[N*2];
struct node
{
    int l, r, nval, eval, nmark, emark;
}s[N*4];
int head[N], siz[N], son[N], top[N], fat[N], dep[N], id[N];
int d[N][2];
int n, m, cnt, num, _case = 0;
void add_edge(int v, int u)
{
    g[cnt].to = u;
    g[cnt].next = head[v];
    head[v] = cnt++;
}
void dfs1(int v, int d, int fa)
{
    siz[v] = 1, son[v] = 0, fat[v] = fa, dep[v] = d;
    for(int i = head[v]; i != -1; i = g[i].next)
    {
        int u = g[i].to;
        if(u != fa)
        {
            dfs1(u, d + 1, v);
            siz[v] += siz[u];
            if(siz[son[v]] < siz[u]) son[v] = u;
        }
    }
}
void dfs2(int v, int tp)
{
    top[v] = tp, id[v] = ++num;
    if(son[v]) dfs2(son[v], tp);
    for(int i = head[v]; i != -1; i = g[i].next)
    {
        int u = g[i].to;
        if(u != fat[v] && u != son[v]) dfs2(u, u);
    }
}
void build(int l, int r, int k)
{
    s[k].l = l, s[k].r = r, s[k].eval = 0, s[k].emark = 0, s[k].nval = 0, s[k].nmark = 0;
    if(l == r) return;
    int mid = (l + r) >> 1;
    build(l, mid, k << 1);
    build(mid + 1, r, k << 1|1);
}
void push_down(int k)
{
    if(s[k].emark)
    {
        s[k<<1].eval += (s[k<<1].r - s[k<<1].l + 1) * s[k].emark;
        s[k<<1|1].eval += (s[k<<1|1].r - s[k<<1|1].l + 1) * s[k].emark;
        s[k<<1].emark += s[k].emark;
        s[k<<1|1].emark += s[k].emark;
        s[k].emark = 0;
    }
    if(s[k].nmark)
    {
        s[k<<1].nval += (s[k<<1].r - s[k<<1].l + 1) * s[k].nmark;
        s[k<<1|1].nval += (s[k<<1|1].r - s[k<<1|1].l + 1) * s[k].nmark;
        s[k<<1].nmark += s[k].nmark;
        s[k<<1|1].nmark += s[k].nmark;
        s[k].nmark = 0;
    }
}
void update(int l, int r, int c, int q, int k)
{
    if(l <= s[k].l && s[k].r <= r)
    {
        if(q == 1)
        {
            s[k].nval += (s[k].r - s[k].l + 1) * c;
            s[k].nmark += c;
        }
        else
        {
            s[k].eval += (s[k].r - s[k].l + 1) * c;
            s[k].emark += c;
        }
        return;
    }
    push_down(k);
    int mid = (s[k].l + s[k].r) >> 1;
    if(l <= mid) update(l, r, c, q, k << 1);
    if(r > mid) update(l, r, c, q, k << 1|1);
}
int query(int x, int q, int k)
{
    if(s[k].l == s[k].r)
    {
        if(q == 1) return s[k].nval;
        else return s[k].eval;
    }
    push_down(k);
    int mid = (s[k].l + s[k].r) >> 1;
    if(x <= mid) return query(x, q, k << 1);
    else return query(x, q, k << 1|1);
}
void renew(int v, int u, int c, int q)
{
    int t1 = top[v], t2 = top[u];
    while(t1 != t2)
    {
        if(dep[t1] < dep[t2])
            swap(t1, t2), swap(v, u);
        update(id[t1], id[v], c, q, 1);
        v = fat[t1], t1 = top[v];
    }
    if(dep[v] > dep[u]) swap(v, u);
    if(q == 1) update(id[v], id[u], c, q, 1);
    else
    {
        if(u != v) update(id[son[v]], id[u], c, q, 1);
    }
}
void slove()
{
    char str[10];
    int a, b, c;
    while(m--)
    {
        scanf(" %s%d%d%d", str, &a, &b, &c);
        if(strcmp(str, "ADD1") == 0)
            renew(a, b, c, 1);
        else renew(a, b, c, 2);
    }
    printf("Case #%d:\n", ++_case);
    for(int i = 1; i <= n; i++)
        if(i != n) printf("%d ", query(id[i], 1, 1));
        else printf("%d\n", query(id[i], 1, 1));
    for(int i = 1; i <= n - 1; i++)
        if(i != n - 1) printf("%d ", query(id[d[i][1]], 2, 1));
        else printf("%d\n", query(id[d[i][1]], 2, 1));
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        memset(head, -1, sizeof head);
        cnt = num = 0;
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n -1; i++)
        {
            scanf("%d%d", &d[i][0], &d[i][1]);
            add_edge(d[i][0], d[i][1]);
            add_edge(d[i][1], d[i][0]);
        }
        dfs1(1, 1, 0);
        dfs2(1, 1);
        for(int i = 1; i <= n - 1; i++)
            if(dep[d[i][0]] > dep[d[i][1]]) swap(d[i][0], d[i][1]);
        build(1, num, 1);
        slove();
    }

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值