【dfs序+线段树】P3178 [HAOI2015]树上操作

这篇具有很好参考价值的文章主要介绍了【dfs序+线段树】P3178 [HAOI2015]树上操作。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

这道题,昨天调到一点多都没调出来,眼睛都要瞎了

今天看着题解边看边调出来了,但是还是感觉不是很会

m d,学的第一道关于树的DS就搞成这样

感觉很寄啊

P3178 [HAOI2015]树上操作 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题意:

【dfs序+线段树】P3178 [HAOI2015]树上操作

【dfs序+线段树】P3178 [HAOI2015]树上操作 

思路:

对于树上的修改操作,我们可以把树的dfs序搞出来,然后在dfs序上修改,这样就把维护树上信息变成维护序列信息了,维护序列的信息用线段树即可

注意,这里的dfs序是每个数仅出现2次,即进栈时记录一次,出栈时记录一次

进栈时记为1,出栈时记为-1

首先维护这么一个序列:

1 1 -1 1 1 -1 1 -1 -1 -1

还有维护dfs序:

1 4 4 2 3 3 5 5 2 1

我们线段树维护的就是这两个序列相乘:

1 4 -4 2 3 -3 5 -5 -2 -1

对于一棵子树,子树就相当于这个序列上的一段区间

操作1是对一个结点u +val,那就是对这个u的进栈的时间戳,即in[u]位置+val,对out[u]位置-val

操作2是对子树整体+val,那么就相当于对区间整体修改,区间中的数,如果是正的就+val,否则就是-val

即区间加:(区间中的所有1+区间中的所有-1)*val

操作3就是输出区间和即可

这些用线段树都非常好维护

【dfs序+线段树】P3178 [HAOI2015]树上操作

【dfs序+线段树】P3178 [HAOI2015]树上操作 

 

Code:文章来源地址https://www.toymoban.com/news/detail-459423.html

#include <bits/stdc++.h>

#define int long long

using namespace std;

const int mxn=2e5+10;
const int mxe=2e5+10;

struct ty{
	int to,next;
}edge[mxe<<2];

struct ty2{
	int val,add;
}tree[mxe<<2];

int N,M,u,v,op,x,k,idx=0,tot=0;
int a[mxn],b[mxn],num[mxn];
int in[mxn],out[mxn],mp[mxn],head[mxn];

void dfs(int u,int fa){
	in[u]=++idx;
	mp[idx]=u;
	b[in[u]]=1;
	for(int i=head[u];~i;i=edge[i].next){
		if(edge[i].to==fa) continue;
		dfs(edge[i].to,u);
	}
	out[u]=++idx;
	mp[idx]=u;
	b[out[u]]=-1;
}
void pushup(int rt){
	tree[rt].val=tree[rt<<1].val+tree[rt<<1|1].val;
}
void build(int rt,int l,int r){
	if(l==r){
		tree[rt].val=b[l]*a[mp[l]];
		return;
	}
	int mid=l+r>>1;
	build(rt<<1,l,mid);
	build(rt<<1|1,mid+1,r);
	pushup(rt);
}
void pushdown(int rt,int l,int r){
	int mid=l+r>>1;
	tree[rt<<1].val+=tree[rt].add*(num[mid]-num[l-1]);
	tree[rt<<1|1].val+=tree[rt].add*(num[r]-num[mid]);
	tree[rt<<1].add+=tree[rt].add;
	tree[rt<<1|1].add+=tree[rt].add;
	tree[rt].add=0;
}
void change(int rt,int l,int r,int x,int k){
	tree[rt].val+=k;
	if(l==r) return;
	int mid=l+r>>1;
	if(x<=mid) change(rt<<1,l,mid,x,k);
	else change(rt<<1|1,mid+1,r,x,k);
}
void modify(int rt,int l,int r,int x,int y,int k){
	if(x<=l&&r<=y){
		tree[rt].add+=k;
		tree[rt].val+=(num[r]-num[l-1])*k;
		return;
	}
	if(tree[rt].add) pushdown(rt,l,r);
	int mid=l+r>>1;
	if(x<=mid) modify(rt<<1,l,mid,x,y,k);
	if(y>mid) modify(rt<<1|1,mid+1,r,x,y,k);
	pushup(rt);
}
int query(int rt,int l,int r,int x,int y){
	if(x<=l&&r<=y){
		return tree[rt].val;
	}
	if(tree[rt].add) pushdown(rt,l,r);
	int mid=l+r>>1;
	int res=0;
	if(x<=mid) res+=query(rt<<1,l,mid,x,y);
	if(y>mid) res+=query(rt<<1|1,mid+1,r,x,y);
	return res;
}
void add(int u,int v){
	edge[tot].to=v;
	edge[tot].next=head[u];
	head[u]=tot++;
}
void G_init(){
	tot=0;
	for(int i=0;i<=N;i++){
		head[i]=-1;
	}
}
void solve(){
	cin>>N>>M;
	G_init();
	for(int i=1;i<=N;i++) cin>>a[i];
	for(int i=1;i<=N-1;i++){
		cin>>u>>v;
		add(u,v);
		add(v,u);
	}
	dfs(1,-1);
	for(int i=1;i<=idx;i++) num[i]=num[i-1]+b[i];
	build(1,1,N+N);
	for(int i=1;i<=M;i++){
		cin>>op;
		if(op==1){
			cin>>x>>k;
			change(1,1,N+N,in[x],k);
			change(1,1,N+N,out[x],-k);
		}else if(op==2){
			cin>>x>>k;
			modify(1,1,N+N,in[x],out[x],k);
		}else{
			cin>>x;
			cout<<query(1,1,N+N,1,in[x])<<'\n';
		}
	}
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int __=1;//cin>>__;
	while(__--)solve();return 0;
}

到了这里,关于【dfs序+线段树】P3178 [HAOI2015]树上操作的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Allegro如何用自带的功能将线段变成铜皮操作指导

    Allegro 如何用自带的功能将线段变成铜皮操作指导   在做PCB设计的时候,有时根据设计需要将线段变成铜皮,可以借助辅助工具来实现这一操作,但是Allegro自身也自带这个功能,如下图 需要把这段走线变成铜皮 具体操作如下 点击File 点击Change Editor

    2024年02月09日
    浏览(27)
  • P4491 [HAOI2018] 染色

    传送门:洛谷 写本题需要知道一个前置知识: 假设恰好选 k k k 个条件的方案数为 f ( k ) f(k) f ( k ) ;先钦定选 k k k 个条件,其他条件无所谓的方案数为 g ( k ) g(k) g ( k ) 那么存在这样的一个关系: g ( k ) = ∑ i = k n C i k f ( i ) g(k)=sum_{i=k}^nC_{i}^kf(i) g ( k ) = ∑ i = k n ​ C i k ​ f ( i ) 上述

    2024年02月07日
    浏览(24)
  • 11.动态规划:树形DP问题、树上最大独立集、树上最小支配集、换根DP、树上倍增(LCA)【灵神基础精讲】

    回溯和树形DP的区别(什么时候需要return结果?):对于回溯,通常是在「递」的过程中增量地构建答案,并在失败时能够回退,例如八皇后。对于递归,是把原问题分解为若干个相似的子问题,通常会在「归」的过程中有一些计算。如果一个递归能考虑用记忆化来优化,就

    2024年02月04日
    浏览(34)
  • P318javascript:void(‘FE2C24‘)3 [HAOI2016] 食物链(记忆化搜索)

    1:思路: 从入读为零的点进行记忆化搜索,搜到出度为零的点返回1,所有返回值加起来就是答案。 2:“注意单独的一种孤立生物不算一条食物链。”出题人都这么好心的说了,然而第一次交的时候还是忘了= =(不然只有20分) 3:ACcode: over~bb

    2024年02月15日
    浏览(24)
  • Mysql时间查询 昨天、今天、上月、本月...

      本文为 【MySQl】 关于时间的查询  📌博主主页:一个肥鲇鱼 👉策略模式之Map+函数式接口:策略模式之Map+函数式接口  👉感受 Lambda 之美:体验一下Lambda之美吧,优雅编程 👉Bean转换工具:Mapstruct使用教程 解释: DATE_SUB函数是MySQL中的一个日期函数,用于在指定的日期上

    2024年02月08日
    浏览(57)
  • 【图论】树上差分(边差分)

    其实点差分和边差分区别不大。 点差分中,d数组存储的是树上的节点 边差分中,d数组存储的是当前节点到父节点的那条边的差分值。 指定注意的是:边差分中因为根连的父节点是虚点,所以遍历结果时应当忽略!       样例输入: 4 1 1 2 2 3 1 4 3 4 样例输出: 3 我们易知:

    2024年02月14日
    浏览(28)
  • 【图论】树上启发式合并

    本篇博客参考: Oi Wiki 树上启发式合并 算法学习笔记(86): 树上启发式合并 首先,什么是 启发式合并 ? 有人将其称为“优雅的暴力”,启发式合并就是在合并两个部分的时候,将内容少的部分合并至内容多的部分,减少合并的操作时间 树上启发式合并(dsu on tree) 可以被用

    2024年04月15日
    浏览(42)
  • 【图论】树上差分(点差分)

    P3128 [USACO15DEC] Max Flow P - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 我们可以先建一棵树 但我们发现,这样会超时。 所以,我们想到树上差分

    2024年02月14日
    浏览(27)
  • 学习笔记——树上哈希

    树上的很多东西都是转化成链上问题的,比如树上哈希 树上哈希,主要是用于树的同构这个东西上的 什么是树的同构? 如图,不考虑节点编号,三棵树是同构的 将树转化成链,一般有两种方式:环游欧拉序与欧拉序 为了尽可能减少哈希冲突,进制位越小越好 又因为不考虑

    2024年02月09日
    浏览(27)
  • 【树上差分+LCA】篮球杯 砍树

    省赛的题现在来补 感觉什么都不会,已经要没了 题意: 思路: 考虑一条边,两端有两棵子树 有这样的性质: 这条边两端的结点的经过次数==M  因此每加一个点对,都对其路径+1 s[u]==M时,与该点连着的边就是合法边了,统计合法边的最大id就行 Code:

    2024年02月06日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包