传送门

•题意

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

  然后有 M 个操作,分为三种:

    操作 1 :把某个节点 x 的点权增加 a 。
    操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
    操作 3 :询问某个节点 x 到根的路径中所有点的点权和。
  输出操作 3 对应的答案;

•题解

  如果可以将树形结构转化成链式结构,那么,操作 1,2 就可以用线段树来维护;

  $1,2,4,4,5,5,2,3,3,1$

  定义 $s,e$ 分别记录每个节点在序列中第一次出现的位置和最后一次出现的位置;

  那么,第一次出现的位置权值为正,最后一次出现的位置权值为负;

  这样的话,就可以通过线段树来维护;

  操作 1 就对应线段树中的单点更新操作;

  操作 2 就对应线段树中的区间更新操作;

  操作 3 就是区间查询操作;

•Code

 #include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define mem(a,b) memset(a,b,sizeof(a))
const int maxn=1e5+; int n,m;
int a[maxn];
int num;
int head[maxn];
struct Edge
{
int to;
int next;
}G[maxn<<];
void addEdge(int u,int v)
{
G[num]={v,head[u]};
head[u]=num++;
}
struct Seg
{
int l,r;
ll lazy;
ll sum;
int x;///沿叶子方向的节点个数
int y;///沿根方向的节点个数
int mid(){return l+((r-l)>>);}
void Set(ll val)
{
///如果[l,r]区间的每个节点都增加val
///那么,[l,r]区间中沿叶子方向的x个节点增加+val
///[l,r]区间中沿根方向的y个节点增加-val
lazy += val;
sum += 1ll*(x-y)*val;
}
}seg[maxn<<];
int cnt;
int s[maxn];
int e[maxn];
int vs[maxn<<];
bool g[maxn<<]; void DFS(int u,int f)
{
vs[++cnt]=u;
s[u]=cnt;
g[cnt]=;
for(int i=head[u];~i;i=G[i].next)
{
int v=G[i].to;
if(v != f)
DFS(v,u);
}
vs[++cnt]=u;
e[u]=cnt;
g[cnt]=;
}
void pushUp(int pos)
{
seg[pos].x=seg[ls(pos)].x+seg[rs(pos)].x;
seg[pos].y=seg[ls(pos)].y+seg[rs(pos)].y;
seg[pos].sum=seg[ls(pos)].sum+seg[rs(pos)].sum;
}
void pushDown(int pos)
{
ll &lazy=seg[pos].lazy;
if(!lazy)
return ; seg[ls(pos)].Set(lazy);
seg[rs(pos)].Set(lazy); lazy=;
}
void build(int l,int r,int pos)
{
seg[pos]={l,r,,,,}; if(l == r)
{
if(g[l])
{
seg[pos].x++;
seg[pos].sum=a[vs[l]];
}
else
{
seg[pos].y++;
seg[pos].sum=-a[vs[l]];
} return ;
} int mid=seg[pos].mid();
build(l,mid,ls(pos));
build(mid+,r,rs(pos)); pushUp(pos);
}
void update(int pos,int l,int r,int x)
{
if(seg[pos].l == l && seg[pos].r == r)
{
seg[pos].Set(x);
return ;
}
pushDown(pos); int mid=seg[pos].mid();
if(r <= mid)
update(ls(pos),l,r,x);
else if(l > mid)
update(rs(pos),l,r,x);
else
{
update(ls(pos),l,mid,x);
update(rs(pos),mid+,r,x);
}
pushUp(pos);
}
ll query(int pos,int l,int r)
{
if(seg[pos].l == l && seg[pos].r == r)
return seg[pos].sum;
pushDown(pos); int mid=seg[pos].mid();
if(r <= mid)
return query(ls(pos),l,r);
else if(l > mid)
return query(rs(pos),l,r);
else
return query(ls(pos),l,mid)+query(rs(pos),mid+,r);
}
void Solve()
{
cnt=;
DFS(,);
build(,cnt,); while(m--)
{
int op;
scanf("%d",&op);
if(op == )
{
int u,x;
scanf("%d%d",&u,&x);
update(,s[u],s[u],x);
update(,e[u],e[u],x);
}
else if(op == )
{
int u,x;
scanf("%d%d",&u,&x);
update(,s[u],e[u],x);
}
else
{
int u;
scanf("%d",&u);
printf("%lld\n",query(,s[],s[u]));
}
}
}
void Init()
{
num=;
mem(head,-);
}
int main()
{
// freopen("C:\\Users\\hyacinthLJP\\Desktop\\C++WorkSpace\\in&&out\\contest","r",stdin);
scanf("%d%d",&n,&m);
for(int i=;i <= n;++i)
scanf("%d",a+i); Init();
for(int i=;i < n;++i)
{
int u,v;
scanf("%d%d",&u,&v);
addEdge(u,v);
addEdge(v,u);
}
Solve(); return ;
}

•变形

  此题操作 3 还可改成求解 $u,v$ 路径间的节点权值和;

  只需在原来的基础上增加个求解 $LCA$ 的代码即可;

BZOJ 4034"树上操作"(DFS序+线段树)的更多相关文章

  1. BZOJ4034 [HAOI2015]树上操作+DFS序+线段树

    参考:https://www.cnblogs.com/liyinggang/p/5965981.html 题意:是一个数据结构题,树上的,用dfs序,变成线性的: 思路:对于每一个节点x,记录其DFS ...

  2. BZOJ 3252题解(贪心+dfs序+线段树)

    题面 传送门 分析 此题做法很多,树形DP,DFS序+线段树,树链剖分都可以做 这里给出DFS序+线段树的代码 我们用线段树维护到根节点路径上节点权值之和的最大值,以及取到最大值的节点编号x 每次从根 ...

  3. [bzoj3306]树——树上倍增+dfs序+线段树

    Brief Description 您需要写一种数据结构,支持: 更改一个点的点权 求一个子树的最小点权 换根 Algorithm Design 我们先忽略第三个要求. 看到要求子树的最小点权,我们想 ...

  4. ACM-ICPC 2018 沈阳赛区网络预赛 J. Ka Chang(树上分块+dfs序+线段树)

    题意 链接:https://nanti.jisuanke.com/t/A1998 给出一个有根树(根是1),有n个结点.初始的时候每个结点的值都是0.下面有q个操作,操作有两种,操作1.将深度为L(根 ...

  5. 洛谷P3178 [HAOI2015]树上操作(dfs序+线段树)

    P3178 [HAOI2015]树上操作 题目链接:https://www.luogu.org/problemnew/show/P3178 题目描述 有一棵点数为 N 的树,以点 1 为根,且树点有边 ...

  6. DFS序+线段树(bzoj 4034)

    题目链接 题目就不多说了. 本题目,可以用dfs序+线段树做:题目给定了一棵树,树上节点告诉了权值.我们可以先将这棵树进行dfs将一棵树变成线性结构:如图 变成这样后,然后就可以用线段树. 操作1:也 ...

  7. [luoguP3178] [HAOI2015]树上操作(dfs序 + 线段树 || 树链剖分)

    传送门 树链剖分固然可以搞. 但还有另一种做法,可以看出,增加一个节点的权值会对以它为根的整棵子树都有影响,相当于给整棵子树增加一个值. 而给以某一节点 x 为根的子树增加一个权值也会影响当前子树,节 ...

  8. 牛客wannafly 挑战赛14 B 前缀查询(trie树上dfs序+线段树)

    牛客wannafly 挑战赛14 B 前缀查询(trie树上dfs序+线段树) 链接:https://ac.nowcoder.com/acm/problem/15706 现在需要您来帮忙维护这个名册, ...

  9. BZOJ4551[Tjoi2016&Heoi2016]树——dfs序+线段树/树链剖分+线段树

    题目描述 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为1),有以下 两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均 ...

  10. BZOJ1103 [POI2007]大都市meg dfs序 线段树

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1103 题意概括 一棵树上,一开始所有的边权值为1,我们要支持两种操作: 1. 修改某一条边的权值为 ...

随机推荐

  1. linux 下批量在多文件中替换字符串

    sed -i "s/原字符串/新字符串/g" `grep 原字符串 -rl 所在目录` 注意:`` 符号在shell里面正式的名称叫做backquote , 一般叫做命令替换其作用 ...

  2. C++学习笔记----4.4 继承情况下的类作用域嵌套

    引言: 在继承情况下,派生类的作用域嵌套在基类作用域中:如果不能在派生类作用域中确定名字,就在外围基类作用域中查找该名字的定义. 正是这种类作用域的层次嵌套使我们能够直接访问基类的成员,就好像这些成员 ...

  3. python学习笔记09--线程、进程

    本节内容 一.进程与线程的概念 1.1进程 1.2线程 1.3进程与线程的区别 二.线程 2.1启一个线程 2.2线程的2种调用方式 2.3 join 2.4 守护线程Daemon 2.5线程锁 2. ...

  4. Direct2D 第6篇 绘制多种风格的线条

    原文:Direct2D 第6篇 绘制多种风格的线条 上图是使用Direct2D绘制的线条,Direct2D在效率上比GDI/GDI+要快几倍,GDI/GDI+绘图是出了名的"慢", ...

  5. ubuntu上制作应用程序的快捷图标启动

    最近在研究Go语言,对比了几种流行的IDE,发现GoLand是使用体验最好的,没有之一.这也印证了网友们常说的那句话“JetBrain出品,必属精品”. 在ubuntu环境下使用GoLand,直接到J ...

  6. PHPCMS快速建站系列之邮箱验证

    1. 登录163邮箱,->设置,开启POP3服务->把SMTP服务器地址复制到PHPCMS后台. 2.开启客户端授权密码 3.填写相关信息,.可以在测试邮箱填入邮箱地址测试

  7. 用python爬虫抓站的一些技巧总结 zz

    用python爬虫抓站的一些技巧总结 zz 学用python也有3个多月了,用得最多的还是各类爬虫脚本:写过抓代理本机验证的脚本,写过在discuz论坛中自动登录自动发贴的脚本,写过自动收邮件的脚本, ...

  8. springboot项目启动,但是访问报404错误

    启动类Application上加了@ComponentScan(basePackages = {})这个注解导致controller扫描不到导致的,如果加了这个注解,springboot就不会扫描Ap ...

  9. 使用 docker-compose 安装 MySQL 5.5 记录

    使用 docker-compose 安装 MySQL 5.5 记录 安装 Docker-Compose 在 Centos 中安装 Docker 倒是很简单. 但是安装 docker-compose 遇 ...

  10. Implement strStr() 字符串匹配

    Implement strStr(). Returns the index of the first occurrence of needle in haystack, or -1 if needle ...