洛谷P3833

摘要:
=top[y]){ifswap(x,y);ans+=query;x=fa[top[x]];}ifswap(x,y);ans+=query;returnans;}操作三区间取最大值intqmax{intans=-101010101;while(top[x]!

Description

树链剖分板子题

考查两种操作

  • A u v w 把 u 节点到 v 节点路径上所有节点权值加 w
  • Q u 求以 u 为根节点的子树权值之和

首先需要了解线段树和 dfs 序,我这里没有很好的链接,不熟悉的再自行百度吧

另外了解树链剖分的思想(重儿子等等),否则会出很多千奇百怪的错误


树链剖分的构成

DFS1 来处理每个点的深度,他的父节点以及他的重儿子

DFS2 来处理每条链的链顶,每个点的 dfs 序和他们的 pre(建树时用)

然后就是各种操作函数


这里就不止说这个题了,顺便说一下树链剖分的其他几种操作

线段树也有很多操作,一些题可能会同时考到

但线段树就不说了,回去翻板子题的教程吧

分享几个树剖典型题目 P2590P3178P4315


Solution

操作一 区间加

树链剖分最常用操作之一

void change1(int x,int y,int val){
		while(top[x]!=top[y]){
		    if(depth[top[x]]<depth[top[y]]) swap(x,y);
			update(1,1,n,val,dfn[top[x]],dfn[x]);
			x=fa[top[x]];
		}
		if(dfn[x]>dfn[y]) swap(x,y);
		update(1,1,n,val,dfn[x],dfn[y]);
	}

操作二 区间求和

int qsum1(int x,int y){
		int ans=0;
		while(top[x]!=top[y]){
		    if(depth[top[x]]<depth[top[y]]) swap(x,y);
			ans+=query(1,1,n,dfn[top[x]],dfn[x]);
			x=fa[top[x]];
		}
		if(dfn[x]>dfn[y]) swap(x,y);
		ans+=query(1,1,n,dfn[x],dfn[y]);
		return ans; 
	}

操作三 区间取最大值

int qmax(int x,int y){
		int ans=-101010101;
		while(top[x]!=top[y]){
		    if(depth[top[x]]<depth[top[y]]) swap(x,y);
			ans=max(ans,qmax(1,1,n,dfn[top[x]],dfn[x]));
			x=fa[top[x]];
		}
		if(dfn[x]>dfn[y]) swap(x,y);
		ans=max(ans,qmax(1,1,n,dfn[x],dfn[y]));
		return ans; 
	}

取最小值也是一样的

不过建树的时候注意处理最大值

操作四 子树上加

在以某点为根节点的子树上加值

	void change2(int x,int val){update(1,dfn[x],dfn[x]+size[x]-1,val,1,n);}

看起来很简单对吧,其实只需要知道他的思想就好了

操作五 子树取和

	int qsum2(int x){return query(1,dfn[x],dfn[x]+size[x]-1,1,n);}


这是我见的比较常用的几种操作

而且一些树链剖分的操作是不用专门来写函数的

就像上面的子树上操作一样


Code

再给下本题的代码,其实不是很必要了

没写注释,大家将就看吧

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<cmath>
#define maxn 10001000
#define INF 0x3f3f3f3f
#define int long long
#define lson x<<1
#define rson x<<1|1

using namespace std;

char s;
int n,q,cnt,tot,lazy[maxn],head[maxn],sum[maxn],a[maxn],size[maxn],dfn[maxn],depth[maxn],top[maxn],fa[maxn],pre[maxn],mmax[maxn],son[maxn];

struct edge{int fr,to,nxt;}e[maxn*2];

void addedge(int fr,int to){e[++tot].to=to;e[tot].nxt=head[fr];head[fr]=tot;}

int read(){
	int s=0,w=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
	while(ch>='0' &&ch<='9'){s=(s<<1)+(s<<3)+ch-'0';ch=getchar();}
	return s*w;
}

namespace Seg{
	void pushup(int x){sum[x]=sum[lson]+sum[rson];}
	
	void pushdown(int x,int ln,int rn){
		if(lazy[x]){
		    lazy[lson]+=lazy[x];
			lazy[rson]+=lazy[x];
			sum[lson]+=lazy[x]*ln;
			sum[rson]+=lazy[x]*rn;
			lazy[x]=0;
		}
	}
	
	void build(int x,int l,int r){
		lazy[x]=0;
		if(l==r){sum[x]=0;return;}
		int mid=(l+r)>>1;
		build(lson,l,mid);build(rson,mid+1,r);
		pushup(x); 
	}
	
	void update(int x,int l,int r,int val,int L,int R){
		if(L<=l &&r<=R){sum[x]+=(r-l+1)*val;lazy[x]+=val;return;}
		int mid=(l+r)>>1;
		pushdown(x,mid-l+1,r-mid);
		if(L<=mid) update(lson,l,mid,val,L,R);
		if(R>mid) update(rson,mid+1,r,val,L,R);			
		pushup(x);
	}
	
	int query(int x,int l,int r,int L,int R){
		if(L<=l &&r<=R) return sum[x];
		int ans=0;
		int mid=(l+r)>>1;
		pushdown(x,mid-l+1,r-mid);
        if(l>R||r<L) return 0;
		if(L<=mid) ans+=query(lson,l,mid,L,R);
		if(R>mid) ans+=query(rson,mid+1,r,L,R);
		return ans;
	}
}

namespace Cut{
	void dfs1(int x,int fat){
		size[x]=1;depth[x]=depth[fat]+1;fa[x]=fat;
		for(int i=head[x];i;i=e[i].nxt){
			int to=e[i].to;
			if(to==fat) continue;
			dfs1(to,x);
			size[x]+=size[to];
			if(size[son[x]]<size[to]) son[x]=to;
		}
	}
	
	void dfs2(int x,int tp){
		top[x]=tp;dfn[x]=++cnt;pre[cnt]=x;
		if(son[x]) dfs2(son[x],tp);
		for(int i=head[x];i;i=e[i].nxt){
			int to=e[i].to;
			if(to==son[x]||to==fa[x]) continue;
			dfs2(to,to);
		}
	}
	
	void change1(int x,int y,int val){
		while(top[x]!=top[y]){
		    if(depth[top[x]]<depth[top[y]]) swap(x,y);
			Seg::update(1,1,n,val,dfn[top[x]],dfn[x]);
			x=fa[top[x]];
		}
		if(dfn[x]>dfn[y]) swap(x,y);
		Seg::update(1,1,n,val,dfn[x],dfn[y]);
	}
	
	int qsum1(int x,int y){
		int ans=0;
		while(top[x]!=top[y]){
		    if(depth[top[x]]<depth[top[y]]) swap(x,y);
			ans+=Seg::query(1,1,n,dfn[top[x]],dfn[x]);
			x=fa[top[x]];
		}
		if(dfn[x]>dfn[y]) swap(x,y);
		ans+=Seg::query(1,1,n,dfn[x],dfn[y]);
		return ans; 
	}
	
	void change2(int x,int val){Seg::update(1,1,n,val,dfn[x],dfn[x]+size[x]-1);}
	int qsum2(int x){return Seg::query(1,1,n,dfn[x],dfn[x]+size[x]-1);}
}

signed main(){
	n=read();
//	for(int i=1;i<=n;i++) a[i]=i-1;
	for(int i=1,fs,es;i<n;i++){fs=read()+1;es=read()+1;addedge(fs,es);addedge(es,fs);}
	Cut::dfs1(1,0);Cut::dfs2(1,1);Seg::build(1,1,n);
	q=read();
	for(int i=1,fs,es,ds;i<=q;i++){
	    cin>>s;
	    if(s =='A'){
	    	fs=read()+1;es=read()+1;ds=read();
			Cut::change1(fs,es,ds); 
		}
		if(s =='Q'){
			fs=read()+1;
			printf("%lld
",Cut::qsum2(fs));
		}
	}
	return 0;
}

ps:

本题每个点的初始权值是 0 ,序号是 1 到 n,

我看成了初始权值为 1 到 n ,然后就 D 了好久

另外注意编号从 0 开始,要在输入加边或者 dfs 建树的地方处理一下


希望对大家有帮助

免责声明:文章转载自《洛谷P3833》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇matplotlib之直接保存图片剖析CPU温度监控技术【转】下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

4.18 省选模拟赛 消息传递 树剖 倍增 线段树维护等比数列

把树剖和倍增 线段树的联系诠释的很完美。 题目意思:自行理解。 做法:设两个点x,y x能挡住y 且在k点处 那么至少的得到一个式子 tx+dx-dk<tx+dy-dk 从这个式子中可以发现 按tx+dx这个排序 前面的可以有可能挡住后面的 同时相等时题目中的要求小的编号优先。 我们考虑一个点挡住当前的这个点的去路 设sx表示x这个点当前不能通过的...

[BZOJ 1568][JSOI2008]Blue Mary开公司

[BZOJ 1568][JSOI2008]Blue Mary开公司 题意 (n) 次操作, 维护一个一次函数集合 (S). 有两种操作: 给定 (b) 和 (k), 向 (S) 中插入一个函数 (f(x)=kx+b). 给定一个 (x), 求 (maxlimits_{fin S}{f(x)}). (nle 1 imes 10^5,xle 50000)....

CF981E Addition on Segments(线段树分治+bitset)

观察本题,我们发现,如果某些操作每次都更新到了某个位置,那么我们可以通过维护bitset来发现这个对于这个位置来说的可能最大值 而且答案就是所有位置取一下或。因为我们发现对于每个位置,bitset上为1的那些数一定可以取到最大值。 但是这样过于暴力,我们发现这是一个区间操作,而区间操作一般和线段树相结合,因此采用线段树分治,先对这段操作影响到的区间打标记...

[POJ1195] Mobile phones(二维树状数组)

题目链接:http://poj.org/problem?id=1195 题意:四种操作: 0:初始化一个S*S的零矩阵 1:点(x,y)是值+A 2:查询一个子矩阵里所有数的和 3:退出 线段树由于不能在两棵树之间传递标记,所以这种求和的操作非常难处理。 改学了一下而为树状数组,发现可是比二维线段树简单多了。 记得之前曾经看过zkw线段树的ppt讲稿,好像...

【uoj#228】基础数据结构练习题 线段树+均摊分析

题目描述 给出一个长度为 $n$ 的序列,支持 $m$ 次操作,操作有三种:区间加、区间开根、区间求和。 $n,m,a_ile 100000$ 。 题解 线段树+均摊分析 对于原来的两个数 $a$ 和 $b$ ( $a>b$ ) ,开根后变成 $sqrt a$ 和 $sqrt b$ ,它们的差从 $a-b$ 变成了 $sqrt a-sqrt b$...

线段树-&amp;gt;面积并 Atlantis HDU

题目链接:https://cn.vjudge.net/problem/HDU-1542 题目大意:求面积并 具体思路:我们首先把矩形分割成一横条一横条的,然后对于每一个我们给定的矩形,我们将储存两个点,一个是左下角,一个是右上角。对于左下角的点,我们将他标记为-1,对于右上角的点,我们把它标记成1,-1代表这块区域的面积是需要减去的,1代表这块区域的面积是...