BZOJ 4034: [HAOI2015]树上操作

Description

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

Input

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

Output

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

Sample Input

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

Sample Output

6

9

13

HINT

对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不会超过 10^6 。

分析

我们注意到,对于一颗树而言,对他进行树剖之后呢,根于其子树的编号一定是连续的,那么我们就可以愉快的树剖了

代码

#include <bits/stdc++.h>

#define N 1000005
#define ll long long

int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}

struct NOTE
{
    int to,next;
}e[N << 1];

int cnt;
int next[N];

void insert(int x,int y)
{
    e[++cnt] = (NOTE){y, next[x]}; next[x] = cnt;
    e[++cnt] = (NOTE){x, next[y]}; next[y] = cnt;
}

struct TREE
{
    ll sum;
    ll lazy;
}t[N];

int n,m;

int top[N];
int fa[N],son[N];
int dep[N],size[N];
int pos[N],mx[N],pre[N];

void dfs1(int x,int d,int f)
{
    dep[x] = d;
    fa[x] = f;
    size[x] = 1;
    for (int i = next[x]; i; i = e[i].next)
    {
        int v = e[i].to;
        if (v == f)
            continue;
        dfs1(v, d + 1, x);
        size[x] += size[v];
        if (!son[x] || size[v] > size[son[x]])
            son[x] = v;
    }
}

int tot;

void dfs2(int x,int k)
{
    top[x] = k;
    pos[x] = ++tot;
    mx[x] = tot;
    pre[pos[x]] = x;
    if (!son[x])
        return;
    dfs2(son[x],k);
    mx[x] = std::max(mx[x], mx[son[x]]);
    for (int i = next[x]; i; i = e[i].next)
    {
        int v = e[i].to;
        if (v != son[x] && v != fa[x])
        {
            dfs2(e[i].to, e[i].to);
            mx[x] = std::max(mx[x], mx[v]);
        }
    }
}

void pushDown(int l,int r,int p)
{
    if (l == r)
        return ;
    int mid = (l + r) >> 1;
    ll tmp = t[p].lazy;
    t[p].lazy = 0;
    t[p * 2].lazy += tmp, t[p * 2 + 1].lazy += tmp;
    t[p * 2].sum += tmp * (mid - l + 1);
    t[p * 2 + 1].sum += tmp * (r - mid);
}

void add(int p,int l,int r,int x,int y,ll val)
{
    if (t[p].lazy)
        pushDown(l,r,p);
    if (l == x && y == r)
    {
        t[p].lazy += val;
        t[p].sum += val * (r - l + 1);
        return;
    }
    int mid = (l + r) >> 1;
    if (x <= mid)
        add(p * 2, l, mid, x, std::min(y, mid), val);
    if (y > mid)
        add(p * 2 + 1, mid + 1, r, std::max(mid + 1, x), y, val);
    t[p].sum = t[p * 2].sum + t[p * 2 + 1].sum;
}

ll query(int p,int l,int r,int x,int y)
{
    if (t[p].lazy)
        pushDown(l,r,p);
    if (l == x && r == y)
        return t[p].sum;
    int mid = (l + r) >> 1;
    ll ans = 0;
    if (x <= mid)
        ans += query(p * 2, l, mid, x, std::min(mid, y));
    if (y > mid)
        ans += query(p * 2 + 1, mid + 1, r, std::max(mid + 1, x), y);
    return ans;
}

ll Query(int x)
{
    ll ans = 0;
    while (top[x] != 1)
    {
        ans += query(1, 1, n, pos[top[x]], pos[x]);
        x = fa[top[x]];
    }
    ans += query(1, 1, n, 1, pos[x]);
    return ans;
}

int v[N];

int main()
{
    n = read(), m = read();
    for (int i = 1; i <= n; i++)
        v[i] = read();
    for (int i = 1; i < n; i++)
    {
        int x = read(), y = read();
        insert(x,y);
    }
    dfs1(1,1,0);
    dfs2(1,1);
    for (int i = 1; i <= n; i++)
        add(1, 1, n, pos[i], pos[i], v[i]);
    int opt,x,a;
    for (int i = 1; i <= m; i++)
    {
        opt = read();
        x = read();
        if (opt == 1)
        {
            a = read();
            add(1 , 1, n, pos[x], pos[x], a);
        }
        if (opt == 2)
        {
            a = read();
            add(1 , 1, n, pos[x], mx[x], a);
        }
        if (opt == 3)
            printf("%lld\n",Query(x));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值