BZOJ 4034"树上操作"(DFS序+线段树)
•题意
有一棵点数为 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序+线段树)的更多相关文章
- BZOJ4034 [HAOI2015]树上操作+DFS序+线段树
参考:https://www.cnblogs.com/liyinggang/p/5965981.html 题意:是一个数据结构题,树上的,用dfs序,变成线性的: 思路:对于每一个节点x,记录其DFS ...
- BZOJ 3252题解(贪心+dfs序+线段树)
题面 传送门 分析 此题做法很多,树形DP,DFS序+线段树,树链剖分都可以做 这里给出DFS序+线段树的代码 我们用线段树维护到根节点路径上节点权值之和的最大值,以及取到最大值的节点编号x 每次从根 ...
- [bzoj3306]树——树上倍增+dfs序+线段树
Brief Description 您需要写一种数据结构,支持: 更改一个点的点权 求一个子树的最小点权 换根 Algorithm Design 我们先忽略第三个要求. 看到要求子树的最小点权,我们想 ...
- ACM-ICPC 2018 沈阳赛区网络预赛 J. Ka Chang(树上分块+dfs序+线段树)
题意 链接:https://nanti.jisuanke.com/t/A1998 给出一个有根树(根是1),有n个结点.初始的时候每个结点的值都是0.下面有q个操作,操作有两种,操作1.将深度为L(根 ...
- 洛谷P3178 [HAOI2015]树上操作(dfs序+线段树)
P3178 [HAOI2015]树上操作 题目链接:https://www.luogu.org/problemnew/show/P3178 题目描述 有一棵点数为 N 的树,以点 1 为根,且树点有边 ...
- DFS序+线段树(bzoj 4034)
题目链接 题目就不多说了. 本题目,可以用dfs序+线段树做:题目给定了一棵树,树上节点告诉了权值.我们可以先将这棵树进行dfs将一棵树变成线性结构:如图 变成这样后,然后就可以用线段树. 操作1:也 ...
- [luoguP3178] [HAOI2015]树上操作(dfs序 + 线段树 || 树链剖分)
传送门 树链剖分固然可以搞. 但还有另一种做法,可以看出,增加一个节点的权值会对以它为根的整棵子树都有影响,相当于给整棵子树增加一个值. 而给以某一节点 x 为根的子树增加一个权值也会影响当前子树,节 ...
- 牛客wannafly 挑战赛14 B 前缀查询(trie树上dfs序+线段树)
牛客wannafly 挑战赛14 B 前缀查询(trie树上dfs序+线段树) 链接:https://ac.nowcoder.com/acm/problem/15706 现在需要您来帮忙维护这个名册, ...
- BZOJ4551[Tjoi2016&Heoi2016]树——dfs序+线段树/树链剖分+线段树
题目描述 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为1),有以下 两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均 ...
- BZOJ1103 [POI2007]大都市meg dfs序 线段树
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1103 题意概括 一棵树上,一开始所有的边权值为1,我们要支持两种操作: 1. 修改某一条边的权值为 ...
随机推荐
- 2018-8-10-C#-不能用于文件名的字符
title author date CreateTime categories C# 不能用于文件名的字符 lindexi 2018-08-10 19:16:52 +0800 2018-02-22 1 ...
- Leetcode844.Backspace String Compare比较含退格的字符串
给定 S 和 T 两个字符串,当它们分别被输入到空白的文本编辑器后,判断二者是否相等,并返回结果. # 代表退格字符. 示例 1: 输入:S = "ab#c", T = " ...
- Pod在多可用区worker节点上的高可用部署
一. 需求分析 当前kubernetes集群中的worker节点可以支持添加多可用区中的ECS,这种部署方式的目的是可以让一个应用的多个pod(至少两个)能够分布在不同的可用区,起码不能分布在同一个可 ...
- Codeforces 442A
题目链接 A. Borya and Hanabi time limit per test 2 seconds memory limit per test 256 megabytes input sta ...
- nodeJs学习-17 博客案例
源码:智能社视频20节课件 const express=require('express'); const static=require('express-static'); const cookie ...
- iOS将image转90,180,270度的方法
http://blog.sina.com.cn/s/blog_6602ffbc0101ckx3.html 这里要分享的是将image旋转,而不是将imageView旋转,原理就是使用quartz2D来 ...
- Python学习之路13☞常用模块
一 time模块 在Python中,通常有这几种方式来表示时间: 时间戳(timestamp):通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量.我们运行“type(t ...
- 手动实现一个form组件
最近研究了一下element-ui,想着手动实现一下里面的form组件,贴个组件里面的代码 <el-form :model="ruleForm" status-icon :r ...
- AutoCAD安装失败怎样卸载重新安装AutoCAD,解决AutoCAD安装失败的方法总结
技术帖:AutoCAD没有按照正确方式卸载,导致AutoCAD安装失败.楼主也查过网上关于如何解决AutoCAD安装失败的一些文章,是说删除几个AutoCAD文件和AutoCAD软件注册表就可以解决A ...
- 字符串分割+二维数组 Day15练习
package com.sxt.arrays.test; import java.util.Arrays; /* 1,2,3,4!5,6,7!8,9!12,456,90!32 * 将此字符串以叹号为分 ...
