BZOJ.4034 [HAOI2015]树上操作 ( 点权树链剖分 线段树 )

题意分析

有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个

操作,分为三种:

操作 1 :把某个节点 x 的点权增加 a 。

操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。

操作 3 :询问某个节点 x 到根的路径中所有点的点权和。

首先上来两次dfs树链剖分问题不大,然后主要是这三个操作分别如何取实现。

先说第一种,点权修改,直接用线段树的点更新就好了。第二个子树修改,貌似树链剖分不能解决子树问题,但是仔细想一下其实是可以的,因为对任意一个节点x,其子树的节点编号都是连续的,原因是在dfs的时候,我们优先遍历这个节点的重儿子形成重链,其次遍历轻儿子形成轻链,所以他的儿子们是连续存储的。而需要修改的区间是[newid[x],newid[x]+size[x]-1],这样利用线段树的区间更新就可已解决了。最后一个询问操作,利用线段树的区间和,完美解决。

直的注意的一点是,在点更新的时候,别忘记有PushDown操作,因为已经打上了lazy标记

代码总览

#include <bits/stdc++.h>
#define ll long long
#define nmax 200820
using namespace std;
int fa[nmax],son[nmax],sz[nmax],newid[nmax],hashback[nmax],dep[nmax],top[nmax],newout[nmax];
int num,tot,head[nmax];
ll data[nmax];
struct edge{
int to;
int next;
}edg[nmax<<2];
struct tre{
int l,r;
ll val,lazy;
int mid(){
return (l+r)>>1;
}
}tree[nmax<<2];
void add(int u, int v){
edg[tot].to = v;
edg[tot].next = head[u];
head[u] = tot++;
}
void dfsFirst(int rt, int f,int d){
dep[rt] = d;
fa[rt] = f;
sz[rt] = 1;
for(int i = head[rt]; i!= -1; i = edg[i].next){
int nxt = edg[i].to;
if(nxt != f){
dfsFirst(nxt,rt,d+1);
sz[rt]+=sz[nxt];
if(son[rt] == -1 || sz[nxt] > sz[son[rt]]){
son[rt] = nxt;
}
}
}
}
void dfsSecond(int rt, int tp){
top[rt] = tp;
newid[rt] = ++num;
hashback[num] = rt;
if(son[rt] == -1) return;
dfsSecond(son[rt],tp);
for(int i = head[rt];i != -1; i = edg[i].next){
int nxt = edg[i].to;
if(nxt != son[rt] && nxt != fa[rt])
dfsSecond(nxt,nxt);
}
}
void init(){
memset(tree,0,sizeof tree);
memset(head,-1,sizeof head);
memset(son,-1,sizeof son);
memset(edg,0,sizeof edg);
memset(hashback,0,sizeof hashback);
memset(data,0,sizeof data);
memset(newid,0,sizeof newid);
tot = num = 0;
}
void PushUp(int rt){
tree[rt].val = tree[rt<<1].val + tree[rt<<1|1].val;
}
void PushDown(int rt){
if(tree[rt].lazy){
tree[rt<<1].lazy += tree[rt].lazy;
tree[rt<<1|1].lazy += tree[rt].lazy;
tree[rt<<1].val += tree[rt].lazy*(ll)(tree[rt<<1].r - tree[rt<<1].l + 1);
tree[rt<<1|1].val +=tree[rt].lazy*(ll)(tree[rt<<1|1].r - tree[rt<<1|1].l + 1);
tree[rt].lazy = 0;
}
}
void Build(int l, int r, int rt){
tree[rt].l = l; tree[rt].r = r;
if(l == r){
tree[rt].val = data[hashback[l]];
return;
}
Build(l,tree[rt].mid(),rt<<1);
Build(tree[rt].mid()+1,r,rt<<1|1);
PushUp(rt);
}
void UpdatePoint(ll val, int pos, int rt){
if(tree[rt].l == tree[rt].r){
tree[rt].val += (ll)val;
return;
}
PushDown(rt);
if(pos <= tree[rt].mid()) UpdatePoint(val,pos,rt<<1);
else UpdatePoint(val,pos,rt<<1|1);
PushUp(rt);
}
void UpdateInterval(ll val, int l, int r, int rt){
if(tree[rt].l >r || tree[rt].r < l) return;
if(tree[rt].l >= l && tree[rt].r <= r){
tree[rt].val += val*(ll)(tree[rt].r - tree[rt].l +1);
tree[rt].lazy += val;
return;
}
PushDown(rt);
UpdateInterval(val,l,r,rt<<1) ;
UpdateInterval(val,l,r,rt<<1|1);
PushUp(rt);
}
ll QuerySUM(int l,int r,int rt){ if(l>tree[rt].r || r<tree[rt].l) return 0;
PushDown(rt);
if(l <= tree[rt].l && tree[rt].r <= r) return tree[rt].val;
return QuerySUM(l,r,rt<<1) + QuerySUM(l,r,rt<<1|1);
}
ll Find_SUM(int x, int y){
int tx = top[x],ty =top[y];
ll ans = 0ll;
while(tx != ty){
if(dep[tx] < dep[ty]){
swap(x,y);
swap(tx,ty);
}
ans += QuerySUM(newid[tx],newid[x],1);
x = fa[tx]; tx = top[x];
}
if(dep[x] > dep[y]) swap(x,y);
ans += QuerySUM(newid[x],newid[y],1);
return ans;
} int n,m;
int main()
{
//freopen("in.txt","r",stdin); while(scanf("%d %d",&n,&m)!=EOF){
init();
for(int i =1;i<=n;++i) scanf("%lld", &data[i]);
int u,v,x,y;
int op;
for(int i =1;i<=n-1;++i){
scanf("%d %d",&u,&v);
add(u,v);
add(v,u);
}
dfsFirst(1,0,1);
dfsSecond(1,1);
Build(1,n,1);
ll val;
for(int i = 0;i<m;++i){
scanf("%d",&op);
if(op == 1){// x add y
scanf("%d %lld",&x,&val);
UpdatePoint(val,newid[x],1);
}else if(op == 2){//x root add y
scanf("%d %lld",&x,&val);
UpdateInterval(val,newid[x],newid[x]+sz[x]-1,1);
}else{// query (1,x)
scanf("%d",&x);
printf("%lld\n",Find_SUM(1,x));
} }
}
return 0;
}

BZOJ.4034 [HAOI2015]树上操作 ( 点权树链剖分 线段树 )的更多相关文章

  1. bzoj 4034 [HAOI2015]树上操作 入栈出栈序+线段树 / 树剖 维护到根距离和

    题目大意 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都 ...

  2. BZOJ.1036 [ZJOI2008]树的统计Count ( 点权树链剖分 线段树维护和与最值)

    BZOJ.1036 [ZJOI2008]树的统计Count (树链剖分 线段树维护和与最值) 题意分析 (题目图片来自于 这里) 第一道树链剖分的题目,谈一下自己的理解. 树链剖分能解决的问题是,题目 ...

  3. fzu 2082 过路费 (树链剖分+线段树 边权)

    Problem 2082 过路费 Accept: 887    Submit: 2881Time Limit: 1000 mSec    Memory Limit : 32768 KB  Proble ...

  4. BZOJ 3589 动态树 (树链剖分+线段树)

    前言 众所周知,90%90\%90%的题目与解法毫无关系. 题意 有一棵有根树,两种操作.一种是子树内每一个点的权值加上一个同一个数,另一种是查询多条路径的并的点权之和. 分析 很容易看出是树链剖分+ ...

  5. bzoj 4196 [Noi2015]软件包管理器 (树链剖分+线段树)

    4196: [Noi2015]软件包管理器 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 2852  Solved: 1668[Submit][Sta ...

  6. BZOJ 3672[NOI2014]购票(树链剖分+线段树维护凸包+斜率优化) + BZOJ 2402 陶陶的难题II (树链剖分+线段树维护凸包+分数规划+斜率优化)

    前言 刚开始看着两道题感觉头皮发麻,后来看看题解,发现挺好理解,只是代码有点长. BZOJ 3672[NOI2014]购票 中文题面,题意略: BZOJ 3672[NOI2014]购票 设f(i)f( ...

  7. POJ.2763 Housewife Wind ( 边权树链剖分 线段树维护区间和 )

    POJ.2763 Housewife Wind ( 边权树链剖分 线段树维护区间和 ) 题意分析 给出n个点,m个询问,和当前位置pos. 先给出n-1条边,u->v以及边权w. 然后有m个询问 ...

  8. bzoj 2157: 旅游【树链剖分+线段树】

    裸的树链剖分+线段树 但是要注意一个地方--我WA了好几次才发现取完相反数之后max值和min值是要交换的-- #include<iostream> #include<cstdio& ...

  9. bzoj4034 (树链剖分+线段树)

    Problem T2 (bzoj4034 HAOI2015) 题目大意 给定一颗树,1为根节点,要求支持三种操作. 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子 ...

随机推荐

  1. 402. Remove K Digits/738.Monotone Increasing Digits/321. Create Maximum Number

    Given a non-negative integer num represented as a string, remove k digits from the number so that th ...

  2. Elasticsearch的停用词(stopwords)

    1.问题 在使用搜索引擎(Elasticsearch或Solr)作为应用的后台搜索平台的时候,会遇到停用词(stopwords)的问题. 在信息检索中,停用词是为节省存储空间和提高搜索效率,处理文本时 ...

  3. 华为笔试——C++最高分问题

    题目介绍:现在输入一组数据,写入学生的考试分数.已知学生数为N,学生编号为1到N,且0<N<=30000,每个学生都有一个分数:操作数为M且0<M<5000.输入第一行为N M ...

  4. PHP处理表单数据的一个安全回顾(记录教训)

    曾经看过一个安全文章中写过这么一条 表单输入数据要做 htmlspecialchars_decode 表单输出数据要做htmlspecialchars 当时还不是很理解为什么,自己也没遇到问题,所以就 ...

  5. 使用git-premit时的问题

    package.json 相关配置如下 { "scripts": { "lint": "eslint pages/* component/* --fi ...

  6. Task 6.2冲刺会议五 /2015-5-18

    今天继续深入的看了看服务器的内容,修改了昨天的代码,发现网络编程还是很好玩的,感觉他的代码比平常写的更有趣一点,另外登陆界面也稍微看了一点.明天准备把登陆界面完善一下.

  7. Linux手动添加系统环境共享库路径

    1.在以下目录 #/etc/ld.so.conf.d 添加相应的xxx.conf 2.写入所要共享库的路径 如:/usr/lib/ 3.导入共享库配置 $ldconfig

  8. 在visual studio中查看源代码

    地址:https://docs.microsoft.com/zh-cn/visualstudio/ide/go-to-and-peek-definition?view=vs-2017 在 Visual ...

  9. vue 使用出现的问题(持续记录)

    今天写vue 的时候,发现有几个警告.原因是 我把组件起的名字写的和默认标签的名字一样了,导致系统不知道,怎么解析. 我写了一个Header 组件, 和h5里面的header重名, 解决方案1: he ...

  10. 复利计算测试(C语言)

    对我们和复利计算程序,写单元测试. 有哪些场景? 期待的返回值 写测试程序. 运行测试. 测试模块 测试输入 预期结果 运行结果 bug跟踪 计算终值 (本金,年限,利率) 终值     1 (100 ...