P3178 [HAOI2015]树上操作

P3178 [HAOI2015]树上操作

题目描述

有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个操作,分为三种:操作 1 :把某个节点 x 的点权增加 a 。操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。操作 3 :询问某个节点 x 到根的路径中所有点的点权和。

输入输出格式

输入格式:

第一行包含两个整数 N, M 。表示点数和操作数。接下来一行 N 个整数,表示树中节点的初始权值。接下来 N-1
行每行两个正整数 from, to , 表示该树中存在一条边 (from, to) 。再接下来 M
行,每行分别表示一次操作。其中第一个数表示该操作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。

输出格式:

对于每个询问操作,输出该询问的答案。答案之间用换行隔开。

输入输出样例

输入样例#1:

5 5
1 2 3 4 5
1 2
1 4
2 3
2 5
3 3
1 2 1
3 5
2 1 2
3 3

输出样例#1:

6
9
13

说明

对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不

会超过 10^6 。

code

好长时间没打过树剖了,来一发练练手QWQ

很裸的区间修改查询

虽然第一次把每条边加了四次全WA

顺便吐槽long long

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define lu u<<1
#define ru u<<1|1
using namespace std;

typedef long long LL;
const int MAXN = 2 * 1e6 + 10;
LL cnt = 0, head[MAXN];
LL N, M, root = 1, tim = 0, a[MAXN], b[MAXN];
LL dep[MAXN], fat[MAXN], son[MAXN];
LL tot[MAXN], top[MAXN], idx[MAXN];

struct Edge {
    LL node, next;
}e[MAXN];

struct Tree {
    LL lef, rig, wei, siz, tag;
}t[MAXN];

inline LL read() {
    LL num = 0, f = 1; char ch = getchar();
    while (!isdigit(ch)) { if (ch == ‘-‘) f = -1; ch = getchar(); }
    while (isdigit(ch)) { num = num * 10 + ch - ‘0‘; ch = getchar(); }
    return num * f;
}

void Add_Edge(LL x, LL y) {
    e[++cnt].next = head[x]; head[x] = cnt; e[cnt].node = y;
}

LL dfs1(LL now, LL fa, LL de) {
    dep[now] = de;
    fat[now] = fa;
    tot[now] = 1;
    LL maxson = -1, v;
    for (LL i = head[now]; i ; i = e[i].next) {
        v = e[i].node;
        if (v == fa) continue;
        tot[now] += dfs1(v, now, de + 1);
        if (tot[v] > maxson) {
            maxson = tot[v];
            son[now] = v;
        }
    }
    return tot[now];
}

void dfs2(LL now, LL topf) {
    idx[now] = ++tim;
    a[tim] = b[now];
    top[now] = topf;
    if (!son[now]) return ;
    dfs2(son[now], topf);
    for (LL i = head[now]; i ; i = e[i].next)
        if (!idx[e[i].node])
            dfs2(e[i].node, e[i].node);
}

void update(LL u) {
    t[u].wei = t[lu].wei + t[ru].wei;
}

void build(LL u, LL l, LL r) {
    t[u].lef = l; t[u].rig = r; t[u].siz = r - l + 1;
    if (l == r) {
        t[u].wei = a[l];
        return ;
    }
    LL mid = (l + r) >> 1;
    build(lu, l, mid);
    build(ru, mid + 1, r);
    update(u);
}

void pushdown(LL u) {
    if (!t[u].tag) return ;
    t[lu].wei += t[lu].siz * t[u].tag;
    t[ru].wei += t[ru].siz * t[u].tag;
    t[lu].tag += t[u].tag;
    t[ru].tag += t[u].tag;
    t[u].tag = 0;
}

void IntervalAdd(LL u, LL l, LL r, LL v) {
    if (l <= t[u].lef && t[u].rig <= r) {
        t[u].wei += t[u].siz * v;
        t[u].tag += v;
        return ;
    }
    LL mid = (t[u].lef + t[u].rig) >> 1;
    pushdown(u);
    if (l <= mid) IntervalAdd(lu, l, r, v);
    if (r > mid)  IntervalAdd(ru, l, r, v);
    update(u);
}

void PointAdd(LL u, LL pos, LL val) {
    if (t[u].lef == t[u].rig) {
        t[u].wei += val;
        return ;
    }
    pushdown(u);
    LL mid = (t[u].lef + t[u].rig) >> 1;
    if (pos <= mid) PointAdd(lu, pos, val);
    if (pos > mid) PointAdd(ru, pos, val);
    update(u);
}

LL IntervalSum(LL u, LL l, LL r) {
    if (l <= t[u].lef && t[u].rig <= r) return t[u].wei;
    LL ans = 0, mid = (t[u].lef + t[u].rig) >> 1;
    pushdown(u);
    if (l <= mid) ans += IntervalSum(lu, l, r);
    if (r > mid)  ans += IntervalSum(ru, l, r);
    return ans;
}

LL TreeSum(LL x, LL y) {
    LL ans = 0;
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        ans += IntervalSum(1, idx[top[x]], idx[x]);
        x = fat[top[x]];
    }
    if (dep[x] > dep[y]) swap(x, y);
    ans += IntervalSum(1, idx[x], idx[y]);
    return ans;
}

int main() {
    memset(head, 0, sizeof (head));
    N = read(); M = read();
    for (LL i = 1; i <= N; ++ i) b[i] = read();
    LL x, y;
    for (LL i = 1; i <= N - 1; ++ i) {
        x = read(); y = read();
        Add_Edge(x, y);
        Add_Edge(y, x);
    }
    dfs1(root, 0, 1);
    dfs2(root, root);
    build(1, 1, N);
    LL kind, val;
    while (M --) {
        kind = read();
        if (kind == 1) {
            x = read(); val = read();
            PointAdd(1, idx[x], val);
        }
        else if (kind == 2) {
            x = read(); val = read();
            IntervalAdd(1, idx[x], idx[x] + tot[x] - 1, val);
        }
        else {
            x = read();
            printf("%lld\n", TreeSum(root, x));
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/hkttg/p/9387624.html

时间: 2024-08-25 10:44:18

P3178 [HAOI2015]树上操作的相关文章

洛谷——P3178 [HAOI2015]树上操作

https://www.luogu.org/problem/show?pid=3178#sub 题目描述 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个操作,分为三种:操作 1 :把某个节点 x 的点权增加 a .操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a .操作 3 :询问某个节点 x 到根的路径中所有点的点权和. 输入输出格式 输入格式: 第一行包含两个整数 N, M .表示点数和操作数.接下来一行 N 个整数,表示树中节点的初始权值.接下来 N-1

洛谷P3178 [HAOI2015]树上操作

题目描述 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个操作,分为三种:操作 1 :把某个节点 x 的点权增加 a .操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a .操作 3 :询问某个节点 x 到根的路径中所有点的点权和. 输入输出格式 输入格式: 第一行包含两个整数 N, M .表示点数和操作数.接下来一行 N 个整数,表示树中节点的初始权值.接下来 N-1 行每行两个正整数 from, to , 表示该树中存在一条边 (from, to) .再接下来

luogu P3178 [HAOI2015]树上操作

题目 题目描述 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有点的点权和. 输入格式 第一行包含两个整数 N, M .表示点数和操作数.接下来一行 N 个整数,表示树中节点的初始权值.接下来 N-1 行每行两个正整数 from, to , 表示该树中存在一条边 (from, to) .再接下来 M

P3178 [HAOI2015]树上操作 (树链剖分模版题)

题目链接:https://www.luogu.org/problem/P3178 一定要用LL  !!!!! 1 #include <stdio.h> 2 #include <cstring> 3 #include <iostream> 4 #include <string> 5 #include <algorithm> 6 #include <queue> 7 #include <vector> 8 #include &

【BZOJ4034】[HAOI2015]树上操作 树链剖分+线段树

[BZOJ4034][HAOI2015]树上操作 Description 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有点的点权和. Input 第一行包含两个整数 N, M .表示点数和操作数. 接下来一行 N 个整数,表示树中节点的初始权值. 接下来 N-1 行每行三个正整数 fr, to

[HAOI2015]树上操作

[HAOI2015]树上操作 2017-09-07 Description 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有点的点权和. Input 第一行包含两个整数 N, M .表示点数和操作数.接下来一行 N 个整数,表示树中节点的初始权值.接下来 N-1 行每行三个正整数 fr, to ,

[HAOI2015]树上操作 -树链剖分

1963. [HAOI2015]树上操作 [题目描述] 有一棵点数为N的树,以点1为根,且树点有权值.然后有M个操作,分为三种: 操作1:把某个节点x的点权增加a. 操作2:把某个节点x为根的子树中所有点的点权都增加a. 操作3:询问某个节点x到根的路径中所有点的点权和. [输入格式] 第一行两个整数N,M,表示点数和操作数. 接下来一行N个整数,表示树中节点的初始权值. 接下来N-1行每行两个正整数fr,to,表示该树中存在一条边(fr,to). 再接下来M行,每行分别表示一次操作.其中第一个

bzoj 4034: [HAOI2015]树上操作 树链剖分+线段树

4034: [HAOI2015]树上操作 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 4352  Solved: 1387[Submit][Status][Discuss] Description 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有

BZOJ 4034 HAOI2015 树上操作

4034: [HAOI2015]树上操作 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 6033  Solved: 1959[Submit][Status][Discuss] Description 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有