【Luogu P4315】月下“毛景树”(树链剖分,线段树)

题目

月下“毛景树”

题目描述

毛毛虫经过及时的变形,最终逃过的一劫,离开了菜妈的菜园。 毛毛虫经过千山万水,历尽千辛万苦,最后来到了小小的绍兴一中的校园里。

爬啊爬~爬啊爬 毛毛虫爬到了一颗小小的“毛景树”下面,发现树上长着他最爱吃的毛毛果 “毛景树”上有N个节点和N-1条树枝,但节点上是没有毛毛果的,毛毛果都是长在树枝上的。但是这棵“毛景树”有着神奇的魔力,他能改变树枝上毛毛果的个数:

  • Change k w:将第k条树枝上毛毛果的个数改变为w个。

  • Cover u v w:将节点u与节点v之间的树枝上毛毛果的个数都改变为w个。

  • Add u v w:将节点u与节点v之间的树枝上毛毛果的个数都增加w个。 由于毛毛虫很贪,于是他会有如下询问:

  • Max u v:询问节点u与节点v之间树枝上毛毛果个数最多有多少个。

输入格式

第一行一个正整数N。

接下来N-1行,每行三个正整数Ui,Vi和Wi,第i+1行描述第i条树枝。表示第i条树枝连接节点Ui和节点Vi,树枝上有Wi个毛毛果。 接下来是操作和询问,以“Stop”结束。

输出格式

对于毛毛虫的每个询问操作,输出一个答案。

样例 #1

样例输入 #1

4
1 2 8
1 3 7
3 4 9
Max 2 4
Cover 2 4 5
Add 1 4 10
Change 1 16
Max 2 4
Stop

样例输出 #1

9
16

提示

1<=N<=100,000,操作+询问数目不超过100,000。

保证在任意时刻,所有树枝上毛毛果的个数都不会超过10^9个。

思路

树链剖分+线段树。
此题维护边权和,只需将边权转化成点权即可。
对于每条边,我们将其权值转化为深度较深的点的点权,这样可以一一对应。
区间查询是,两点的 LCA 的点权是不可以取的,它所对应的边并不在两点路径上。
所以树链剖分的最后一次操作,区间应为 i d [ x ] + 1 ∼ i d [ y ] id[x]+1 \sim id[y] id[x]+1id[y]
接下来就是码码码了。
要考虑清楚懒标记的下传。区间覆盖的懒标记优先级更高。如果区间覆盖要下传,左右儿子区间加的懒标记就要被清零了!

然后又调了好长时间,结果发现单点修改时下标搞错了,应该是树剖后的 i d [ x ] id[x] id[x],而非 x x x

代码

#include <bits/stdc++.h>
#define rep(i, a, b) for (register int i(a); i <= b; ++i)
#define per(i, a, b) for (register int i(a); i >= b; --i)
#define FILE(s) freopen(s".in", "r", stdin), freopen(s".out", "w", stdout)
#define mem(a, x) memset(a, x, sizeof a)
#define pb push_back
#define umap unordered_map
#define pqueue priority_queue
#define mp make_pair
#define PI acos(-1)
//#define int long long

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int n, dep[100005], siz[100005], son[100005], top[100005], father[100005], a[100005], id[100005], w[100005], tot;
vector <int> e[100005];
struct enode {
    int x, y, z;
} ed[100005];

template <typename _T>
void rd(_T &x) {
    int f = 1; x = 0;
    char s = getchar();
    while (s > '9' || s < '0') {if (s == '-') f = -1; s = getchar();}
    while (s >= '0' && s <= '9') x = (x<<3)+(x<<1)+(s^48), s = getchar();
    x *= f;
}

template <typename _T>
void write(_T x) {
    if (x < 0) putchar('-'), x = -x;
    if (x > 9) write(x/10);
    putchar(x%10+'0');
    return ;
}

struct seg {
    int tr[400005], tag[400005], tag2[400005];

    #define ls k<<1
    #define rs k<<1|1

    inline void pushup(int k) {
        tr[k] = max(tr[ls], tr[rs]);
        return ;
    }

    inline void pushdown(int k) {
        int x = tag2[k];
        if (x >= 0) tag2[ls] = tag2[rs] = x, tr[ls] = tr[rs] = x, tag[ls] = tag[rs] = 0;
        tag2[k] = -1;
        x = tag[k];
        tag[k] = 0;
        tag[ls] += x, tag[rs] += x;
        tr[ls] += x, tr[rs] += x;
        return ;
    }

    void build(int k, int l, int r) {
        tag2[k] = -1;
        if (l == r) {
            tr[k] = w[l];
            return ;
        }
        int mid = (l+r) >> 1;
        build(ls, l, mid); build(rs, mid+1, r);
        pushup(k);
        return ;
    }

    void modify_p(int k, int l, int r, int pos, int c) {
        if (l == r) {
            tr[k] = c;
            return ;
        }
        pushdown(k);
        int mid = (l+r) >> 1;
        if (pos <= mid) modify_p(ls, l, mid, pos, c);
        else modify_p(rs, mid+1, r, pos, c);
        pushup(k);
        return ;
    }
    
    void modify(int k, int l, int r, int L, int R, int c) {
        if (L > R) return ;
        if (l >= L && r <= R) {
            tr[k] = c;
            tag2[k] = c;
            tag[k] = 0;
            return ;
        }
        pushdown(k);
        int mid = (l+r) >> 1;
        if (L <= mid) modify(ls, l, mid, L, R, c);
        if (R > mid) modify(rs, mid+1, r, L, R, c);
        pushup(k);
        return ;
    }

    void add(int k, int l, int r, int L, int R, int c) {
        if (L > R) return ;
        if (l >= L && r <= R) {
            tr[k] += c;
            tag[k] += c;
            return ;
        }
        pushdown(k);
        int mid = (l+r) >> 1;
        if (L <= mid) add(ls, l, mid, L, R, c);
        if (R > mid) add(rs, mid+1, r, L, R, c);
        pushup(k);
        return ;
    }

    int query(int k, int l, int r, int L, int R) {
        if (L > R) return -1;
        if (l >= L && r <= R) return tr[k];
        pushdown(k);
        int mid = (l+r) >> 1, ret = 0;
        if (L <= mid) ret = max(ret, query(ls, l, mid, L, R));
        if (R > mid) ret = max(ret, query(rs, mid+1, r, L, R));
        return ret;
    }
} Tr;

void dfs1(int x, int fa) {
    dep[x] = dep[fa]+1;
    father[x] = fa;
    siz[x] = 1;
    int maxn = 0;
    for (auto y : e[x]) {
        if (y == fa) continue;
        dfs1(y, x);
        siz[x] += siz[y];
        if (maxn < siz[y]) maxn = siz[y], son[x] = y;
    }
    return ;
}

void dfs2(int x, int fa, int tp) {
    top[x] = tp;
    id[x] = ++tot;
    w[tot] = a[x];
    if (son[x] == 0) return ;
    dfs2(son[x], x, tp);
    for (auto y : e[x]) {
        if (y == fa || y == son[x]) continue;
        dfs2(y, x, y);
    }
    return ;
}

int query_road(int x, int y) {
    int ret = 0;
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) x ^= y ^= x ^= y;
        ret = max(ret, Tr.query(1, 1, n, id[top[x]], id[x]));
        x = father[top[x]];
    }
    if (dep[x] > dep[y]) x ^= y ^= x ^= y;
    ret = max(ret, Tr.query(1, 1, n, id[x]+1, id[y]));
    return ret;
}

void cover_road(int x, int y, int z) {
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) x ^= y ^= x ^= y;
        Tr.modify(1, 1, n, id[top[x]], id[x], z);
        x = father[top[x]];
    }
    if (dep[x] > dep[y]) x ^= y ^= x ^= y;
    Tr.modify(1, 1, n, id[x]+1, id[y], z);
    return ;
}

void modify_road(int x, int y, int z) {
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) x ^= y ^= x ^= y;
        Tr.add(1, 1, n, id[top[x]], id[x], z);
        x = father[top[x]];
    }
    if (dep[x] > dep[y]) x ^= y ^= x ^= y;
    Tr.add(1, 1, n, id[x]+1, id[y], z);
    return ;
}

int main() {
    rd(n);
    rep(i, 1, n-1) {
        int x, y, z; rd(x), rd(y), rd(z);
        e[x].pb(y), e[y].pb(x);
        ed[i].x = x, ed[i].y = y, ed[i].z = z;
    }
    dfs1(1, 0);
    rep(i, 1, n-1) {
        if (ed[i].x == father[ed[i].y]) a[ed[i].y] = ed[i].z;
        else a[ed[i].x] = ed[i].z;
    }
    dfs2(1, 0, 1);
    Tr.build(1, 1, n);
    while (true) {
        char s[11]; scanf("%s", s);
        if (s[0] == 'S') break;
        if (s[0] == 'M') {
            int u, v; rd(u), rd(v);
            printf("%d\n", query_road(u, v));
        } else if (s[0] == 'C' && s[1] == 'o') {
            int u, v, w; rd(u), rd(v), rd(w);
            cover_road(u, v, w);
        } else if (s[0] == 'C' && s[1] == 'h') {
            int u, w; rd(u), rd(w);
            int x;
            if (ed[u].x == father[ed[u].y]) x = ed[u].y;
            else x = ed[u].x;
            Tr.modify_p(1, 1, n, id[x], w);
        } else {
            int u, v, w; rd(u), rd(v), rd(w);
            modify_road(u, v, w);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值