Codeforces 276E(树状数组)

题意:一棵树有n个节点,1是根节点,根节点的子节点是单链,然后现在有两种操作0 v x d表示距离节点v为d的节点权值都加x,操作1 v问v节点的权值,初始节点权值都是0。

题解:看了别人的题解才会的,维护两种树,把每条单链都当做一个树状数组维护当前链上每个节点的权值,另一种是从根节点开始维护距离为x的节点的权值。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define ll long long
using namespace std;
const int N = 100005;
int n, q, dis[N], root[N];//数组存深度和单链头节点
vector<int> g[N];
vector<vector<ll> > C;

int lowbit(int x) {
    return x & (-x);
}
//对于根节点的树,en代表距离根节点的长度,如果等于0就不加,因为变量sum加过了
//对于单链的树,st到en向下延伸
void Add(int st, int en, int val, int cur) {
    if (st > en)
        return;
    int sz = C[cur].size();
    for (int i = st; i < sz; i += lowbit(i))
        C[cur][i] += val;
    for (int i = en + 1; i < sz; i += lowbit(i))
        C[cur][i] -= val;
}

ll Sum(int deep, int cur) {
    ll ret = 0;
    int sz = C[cur].size();
    for (int i = deep; i > 0; i -= lowbit(i))
        ret += C[cur][i];
    return ret;
}

void dfs(int u, int pre, int cnt, int cur) {
    dis[u] = cnt;
    root[u] = cur;
    C[cur].push_back(0);
    int sz = g[u].size();
    for (int i = 0; i < sz; i++) {
        int v = g[u][i];
        if (v != pre)
            dfs(v, u, cnt + 1, cur);
    }
}

int main() {
    scanf("%d%d", &n, &q);
    for (int i = 0; i <= n; i++)
        g[i].clear();
    int a, b, op, l, r, x, v, d;
    for (int i = 1; i < n; i++) {
        scanf("%d%d", &a, &b);
        g[a].push_back(b);
        g[b].push_back(a);
    }
    int sz = g[1].size();
    C.resize(sz + 5);
    for (int i = 0; i < sz; i++) {
        C[i + 1].push_back(0);//根节点
        dfs(g[1][i], 1, 1, i + 1);
    }
    C[0].resize(N, 0);
    ll sum = 0;//根节点1的权值
    while (q--) {
        scanf("%d", &op);
        if (op == 0) {
            scanf("%d%d%d", &v, &x, &d);
            int cur = root[v];
            if (d < dis[v])//不会延伸到其他链
                Add(dis[v] - d, dis[v] + d, x, cur);
            else {
                sum += x;
                Add(1, dis[v] + d, x, cur);//单链的子节点延伸
                Add(1, d - dis[v], x, 0);//向根节点延伸
                Add(1, d - dis[v], -x, cur);//重复加的减掉
            }
        }
        else {
            scanf("%d", &v);
            if (v == 1)
                printf("%lld\n", sum);
            else {
                int cur = root[v];
                printf("%lld\n", Sum(dis[v], cur) + Sum(dis[v], 0));//向根节点延伸的单链上的和加上根节点向下延伸的加上的权值
            }
        }
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-01 20:23:48

Codeforces 276E(树状数组)的相关文章

HDU 3333 | Codeforces 703D 树状数组、离散化

HDU 3333:http://blog.csdn.net/julyana_lin/article/details/7877164 这两个题是类似的,都是离线处理查询,对每次查询的区间的右端点进行排序.这里我们需要离散化处理一下,标记一下前面是否出现过这个值,然后不断更新last数组(该数组保存的是每个数最后一次出现的位置).最后用树状数组维护. 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4

CodeForces 396C 树状数组 + DFS

这题目一开始看到了就想到了线段树或者树状数组,但是对于一个节点的所有子节点加权有所疑惑,后来看到根树这个条件,就像到了 那么1号点肯定在第一层,那么建立单向边往下搜,然后记录一下 每一个节点所在的 层,最后 两个节点 相差的 层数就知道了,就容易加权处理了,然后就开始建立数组了,后来一直爆错,后来发现 是范围有问题,这样直接建立是错的,因为不知道具体范围,数字太大了, 所以参考了一下 http://blog.csdn.net/keshuai19940722/article/details/201

codeforces 629D 树状数组+LIS

题意:n个圆柱形蛋糕,给你半径 r 和高度 h,一个蛋糕只能放在一个体积比它小而且序号小于它的蛋糕上面,问你这样形成的上升序列中,体积和最大是多少 分析:根据他们的体积进行离散化,然后建树状数组,按照序号进行循环,每次查询体积比它小的蛋糕形成的最大体积 注:因为是按照序号进行循环,所以序号一定是严格小于它的,时间复杂度O(nlogn) #include <iostream> #include <cstdio> #include <algorithm> using nam

Encryption (hard) CodeForces - 958C3 (树状数组)

大意: 给定序列$a$, 要求将$a$分成$k$个非空区间, 使得区间和模$p$的和最小, 要求输出最小值. $k$和$p$比较小, 直接暴力$dp$, 时间复杂度是$O(nklogp)$, 空间是$O(nk+kp)$ $dp[i][j]=min(...,f[j-1][s[i]-1]+1,f[j][s[i]],f[j][s[i]+1]-1+p,...)$ 看了其他提交, 好像有$O(nk)$的做法. #include <iostream> #include <sstream> #i

CodeForces 380C Sereja and Brackets(扫描线+树状数组)

[题目链接] http://codeforces.com/problemset/problem/380/C [题目大意] 给出一个括号序列,求区间内左右括号匹配的个数. [题解] 我们发现对于每个右括号,其匹配的左括号是固定的, 我们保存每个右括号匹配的左括号位置, 对区间询问进行线扫描,将扫描的区间右端点及其之前所有的右括号对应的左括号位置做标记, 只要查询询问区间的标记个数就是答案,这个可以用树状数组维护. [代码] #include <cstdio> #include <algor

Codeforces 597C. Subsequences (树状数组+dp)

题目链接:http://codeforces.com/contest/597/problem/C 给你n和数(1~n各不同),问你长为k+1的上升自序列有多少. dp[i][j] 表示末尾数字为i 长度为j的上升子序列个数,但是dp数组是在树状数组的update函数中进行更新. update(i, val, j)函数表示在i的位置加上val,更新dp[i][j]. sum(i, j)就是求出末尾数字小于等于i 且长度为j的子序列有多少个. 1 //#pragma comment(linker,

Codeforces Round #277 E. LIS of Sequence(486E) 树状数组乱搞

http://codeforces.com/contest/486/problem/E E. LIS of Sequence time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output The next "Data Structures and Algorithms" lesson will be about Longest I

Codeforces Round #413, rated, Div. 1 + Div. 2 C. Fountains(贪心 or 树状数组)

http://codeforces.com/contest/799/problem/C 题意: 有n做花园,有人有c个硬币,d个钻石 (2 ≤ n ≤ 100 000, 0 ≤ c, d ≤ 100 000) ,每一个花园用三个维度描述(a,b,c),分别是美丽度,所花钱币个数,钱币种类,当然,钱币之间不能兑换,该人必须要建筑两座花园,如果可以,输出两座花园总的美丽度,否则输出0: 思路: 首先想到分三种情况讨论,两个花园都用硬币,两个花园都用钻石,一个用钻石一个用硬币. 大神的代码真的是很厉害

【树状数组】Codeforces Round #755 D. PolandBall and Polygon

http://codeforces.com/problemset/problem/755/D 每次新画一条对角线的时候,考虑其跨越了几条原有的对角线. 可以用树状数组区间修改点查询来维护多边形的顶点.答案每次增加 新对角线的左端点在多少个区间内+右端点在多少个区间内+1,每次把新画的对角线所覆盖的较小区间内部的每个点加1即可.注意,一定要是较小的区间,因为这样才能保证其左右端点不同时在区间内,不会重复统计. #include<cstdio> #include<algorithm>