首页 > 其他分享 >树链剖分模板(为什么我的这么长???)

树链剖分模板(为什么我的这么长???)

时间:2023-03-06 10:11:37浏览次数:41  
标签:剖分 tree long 树链 Seg HL now id 模板

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>

#define lid id << 1
#define rid id << 1 | 1

using namespace std;

long long n, m, r, rank_[5211314], new_id;
//rank_表示修改后的序号对应的先前序号 new表示新序号 
long long cnt, head[5211314], w[5211314];
//head表示第i个点第一条边的cnt 

struct Chain_Forward_Star {
	long long next;//下一条边的cnt 
	long long to;//这个cnt边连接的下一个节点 
}pic[5211314];

struct Heavy_Light_Decompostion {
	long long Father;//保存结点的父亲节点
	long long Deep;//深度 
	long long Size;//子树节点个数 
	long long Heavy_Son;//重儿子 
	long long Top;//重链顶节点(若无重链则是他本身)
	long long Id;//树链剖分后新编号 
}HL_tree[5211314];
//树链剖分 

struct Segment_tree {
	long long l, r;
	long long lazy, sum, maxn;
}Seg_tree[5211314];

inline long long read() {
	long long x = 0, f = 1;
	char ch = getchar();
	while ( ch < '0' || ch > '9' ) {
		if ( ch == '-' ) f = -1;
		ch = getchar();
	}
	while ( ch >= '0' && ch <= '9' ) {
		x = ( x << 1 ) + ( x << 3 ) + ( ch -'0' );
		ch = getchar();
	}
	return x * f;
}//快读 

void print( long long x ) {
	if ( x < 0 ) putchar( '-' ), x *= -1;
	if ( x > 9 ) print( x / 10 );
	putchar( x % 10 + '0' );
	return;
}//快输 

inline void add_edge( long long v1 , long long v2 ) {
	++ cnt;
	pic[cnt].next = head[v1];
	head[v1] = cnt;
	pic[cnt].to = v2;
	return; 
}//链式前向星建边 

inline void pushdown( long long id ) {
	if ( Seg_tree[id].lazy ) {
		long long temp = Seg_tree[id].lazy;
		Seg_tree[lid].lazy += temp;
		Seg_tree[rid].lazy += temp;
		Seg_tree[lid].sum += temp * ( Seg_tree[lid].r - Seg_tree[lid].l + 1 ); 
		Seg_tree[rid].sum += temp * ( Seg_tree[rid].r - Seg_tree[rid].l + 1 );
		Seg_tree[lid].maxn += temp;
		Seg_tree[rid].maxn += temp;
		Seg_tree[id].lazy = 0; 
	}//写到这里 
}

inline void pushup( long long id ) {
	Seg_tree[id].sum = Seg_tree[lid].sum + Seg_tree[rid].sum;
	Seg_tree[id].maxn = max( Seg_tree[lid].maxn , Seg_tree[rid].maxn );
	return;
}

void build_tree( long long id , long long l , long long r ) {
	Seg_tree[id].l = l;
	Seg_tree[id].r = r;
	long long mid = ( l + r ) >> 1;
	if ( l == r ) {
		Seg_tree[id].sum = Seg_tree[id].maxn = w[rank_[l]];
		//因为是按第二次DFS序找,注意这里是w[rank_[l]]
		return;
	}
	build_tree( lid , l , mid );
	build_tree( rid , mid + 1 , r );
	pushup( id );
	return;
}

void update( long long id , long long l , long long r , long long add_num ) {
	if ( Seg_tree[id].l >= l && Seg_tree[id].r <= r ) {
		Seg_tree[id].sum += add_num * ( Seg_tree[id].r - Seg_tree[id].l + 1 );
		Seg_tree[id].maxn += add_num;
		Seg_tree[id].lazy += add_num;
		return;
	}
	pushdown( id );
	long long mid = ( Seg_tree[id].l + Seg_tree[id].r ) >> 1;
	if ( l <= mid ) update( lid , l , r , add_num );
	if ( r > mid ) update( rid , l , r , add_num );
	pushup( id );
	return;
}

long long segtree_query_sum( long long id , long long l , long long r ) {
	if ( Seg_tree[id].l >= l && Seg_tree[id].r <= r ) {
		return Seg_tree[id].sum;
	}
	pushdown( id );
	long long ans = 0;
	long long mid = ( Seg_tree[id].l + Seg_tree[id].r ) >> 1;
	if ( l <= mid ) ans += segtree_query_sum( lid , l , r );
	if ( r > mid )  ans += segtree_query_sum( rid , l , r );
	return ans;
}

long long segtree_query_max( long long id , long long l , long long r ) {
	if ( Seg_tree[id].l >= l && Seg_tree[id].r <= r ) {
		return Seg_tree[id].maxn;
	}
	pushdown( id );
	long long ans = -521131400000;
	long long mid = ( Seg_tree[id].l + Seg_tree[id].r ) >> 1;
	if ( l <= mid ) ans = max( ans , segtree_query_max( lid , l , r ) );
	if ( r > mid )  ans = max( ans , segtree_query_max( rid , l , r ) );
	return ans;  
}

inline long long query_sum( long long x , long long y ) {
	long long ans = 0;
	long long left, right;
	while ( HL_tree[x].Top != HL_tree[y].Top ) {
		//不在一条链里
		if ( HL_tree[HL_tree[x].Top].Deep < HL_tree[HL_tree[y].Top].Deep ) {
			swap( x , y );
			//以链顶深度大的向上走 
		} 
		left = HL_tree[HL_tree[x].Top].Id;
		right = HL_tree[x].Id;
		//x这条链的左右Id区间 
		ans = ans + segtree_query_sum( 1 , left , right );
		//查询这条链的和
		x = HL_tree[HL_tree[x].Top].Father;
		//x跳到链顶的父亲 
	}
	//跳出while时x与y在同一条重链上了 
	if ( HL_tree[x].Deep > HL_tree[y].Deep ) {
		swap( x , y );
		//让x成为y的祖先 
	}
	left = HL_tree[x].Id;
	right = HL_tree[y].Id;
	ans += segtree_query_sum( 1 , left , right );
	return ans;
}//找x到y最短路里的和 

inline long long query_max( long long x , long long y ) {
	long long ans = -521131400000;
	long long left, right;
	while ( HL_tree[x].Top != HL_tree[y].Top ) {
		if ( HL_tree[HL_tree[x].Top].Deep < HL_tree[HL_tree[y].Top].Deep ) {
			swap( x , y );
		}
		left = HL_tree[HL_tree[x].Top].Id;
		right = HL_tree[x].Id;
		ans = max( ans , segtree_query_max( 1 , left , right ) );
		x = HL_tree[HL_tree[x].Top].Father;
	}
	if ( HL_tree[x].Deep > HL_tree[y].Deep ) {
		swap( x , y );
	}
	left = HL_tree[x].Id;
	right = HL_tree[y].Id;
	ans = max( ans , segtree_query_max( 1 , left , right ) );
	return ans;
}//找x到y最短路里的的最大值 

inline void HL_update( long long x , long long y ,long long add_num ) {
	long long left, right;
	while ( HL_tree[x].Top != HL_tree[y].Top ) {
		if ( HL_tree[HL_tree[x].Top].Deep < HL_tree[HL_tree[y].Top].Deep ) {
			swap( x , y );
		}
		left = HL_tree[HL_tree[x].Top].Id;
		right = HL_tree[x].Id;
		update( 1 , left , right , add_num );
		x = HL_tree[HL_tree[x].Top].Father;
	}
	if ( HL_tree[x].Deep > HL_tree[y].Deep ) {
		swap( x , y );
	}
	left = HL_tree[x].Id;
	right = HL_tree[y].Id;
	update( 1 , left , right , add_num );
	return;
}//把x到y的最短路里的每一个节点都加上add_num 

void DFS_1( long long now , long long fa ) {
	//now表示当前节点 fa表示爹 
	HL_tree[now].Father = fa;//now对应的父亲 
	HL_tree[now].Deep = HL_tree[fa].Deep + 1;//深度是父亲深度+1 
	HL_tree[now].Size = 1;//大小初始化为1(要把自己算进去) 
	for (long long i = head[now], v; i != 0; i = pic[i].next) {
		//遍历边 
		v = pic[i].to;
		if ( v != fa ) {
			//因为是无向图,所以要注意可能有指向fa的边 
			DFS_1( v , now );
			HL_tree[now].Size += HL_tree[v].Size;//子树已处理,所以要加上子树大小
			if ( HL_tree[v].Size > HL_tree[HL_tree[now].Heavy_Son].Size ) {
				HL_tree[now].Heavy_Son = v;
			} 
		}
	}
	return;
}

void DFS_2( long long now , long long top ) {
	//now表示当前节点 top表示当前链顶 
	HL_tree[now].Top = top;
	HL_tree[now].Id = ++ new_id;
	rank_[new_id] = now;
	if ( HL_tree[now].Heavy_Son ) DFS_2( HL_tree[now].Heavy_Son , top );
	//先走重儿子,目的是使重链轻链的的dfs序连续 
	for (long long i = head[now], v; i != 0; i = pic[i].next) {
		v = pic[i].to;
		if ( v != HL_tree[now].Father && v != HL_tree[now].Heavy_Son ) {
			//第一个判断防止在无向图中下一个儿子自己父亲
			//第二个判断表示儿子不在重链里,即不是重儿子
			DFS_2( v , v );
			//儿子不是重儿子则重链顶是自己 
		}
	}
	return; 
}

int main()
{
	n = read();
	m = read();
	r = read();
	for (int i = 1; i <= n; ++i) {
		w[i] = read();
	}
	for (int i = 1, a, b; i <= n - 1; ++i) {
		a = read();
		b = read();
		add_edge( a , b );
		add_edge( b , a );
	}
	DFS_1( r , 0 );
	DFS_2( r , r );
	build_tree( 1 , 1 , n );
	for (long long i = 1, x, y, z, p; i <= m; ++i) {
		p = read();
		if ( p == 1 ) {
			//表示将树从 x 到 y 结点最短路径上所有节点的值都加上 z
			x = read();
			y = read();
			z = read();
			HL_update( x , y , z );
		}
		else if ( p == 2 ) {
			//表示求树从 x 到 y 结点最短路径上所有节点的值之和
			x = read();
			y = read();
			print( query_sum( x , y ) ); 
			putchar( '\n' );
		}
		else if ( p == 3 ) {
		//表示将以 x 为根节点的子树内所有节点值都加上 z
			x = read();
			z = read();
			update( 1 , HL_tree[x].Id , HL_tree[x].Id + HL_tree[x].Size - 1 , z );
			
		}
		else if ( p == 4 ) {
			// 表示求以 x 为根节点的子树内所有节点值之和
			x = read();
			print( segtree_query_sum( 1 , HL_tree[x].Id , HL_tree[x].Id + HL_tree[x].Size - 1 ) );
			putchar( '\n' );
		}
	}
	return 0;
}

标签:剖分,tree,long,树链,Seg,HL,now,id,模板
From: https://www.cnblogs.com/jueqingfeng/p/17182775.html

相关文章

  • 使用注解开发SpringMVC,也是以后开发的模板(重点)
    注解版配置SpringMVC(重点)第一步:新建一个moudel,添加web支持!建立包结构top.lostyou.controller第二步:由于maven可能存在资源过滤问题,我们将配置完善<!--在build中......
  • 浅谈二分标准模板以及边界变形
    1.5、搜索算法1.5.1、折半搜索(二分)二分的基础的用法是在单调序列或单调函数中进行查找。因此当问题的答案具有单调性时,就可以通过二分把求解转化为判定。1.5.1.1、标准......
  • 整数二分模板
    文章目录​​Question​​​​Ideas​​​​Code​​​​C++​​​​Python​​Question给定一个按照升序排列的长度为n的整数数组,以及q个查询。对于每个查询,返回一个元......
  • vscode快捷生成vue模板
    1,文件-->首选项-->用户代码片段-->点击新建代码片段--取名vue.json确定2,粘贴模板:{"Printtoconsole":{"prefix":"vue","body":[......
  • EasyCode mybatis-plus模板 &Live tmpl
    Mapper##导入宏定义$!{define.vm}##设置表后缀(宏定义)#setTableSuffix("Mapper")##保存文件(宏定义)#save("/mapper","Mapper.java")##包路径(宏定义)#setPackageS......
  • 线段树模板
    structSegmentTree{voidpushUp(intu){maxv[u]=std::max(maxv[u<<1],maxv[u<<1|1]);}voidcoverDown(intu){if(lz1[u]......
  • ssh框架整合模板
    Spring2.5+Hibernate3.3+Struts1.3整合开发struts-config.xml:<?xmlversion="1.0"encoding="utf-8"?><!DOCTYPEstruts-configPUBLIC"-//ApacheSoftwareFoundation//D......
  • 树链剖分
    终于学到了树剖!!!前置知识:LCA,树形DP,DFS,邻接表,线段树。 树链剖分线段树的特点:区间修改,区间查询,线性;树上差分特点:单点修改,树形区间查询;现在,如果我们想进行树形区间修改......
  • 并查集模板
    #include<bits/stdc++.h>usingnamespacestd;intfa[10005];intm,n;intfind(intx){ if(fa[x]!=x){ fa[x]=find(fa[x]); } returnfa[x];}booljudge(intx,......
  • 区间DP模板
    区间dp一般都比较死板DP[i][len]表示从i开始,长度为len 区间dp通常数据N为300,400,500---几百的大小 for(intlen=2;len<=n;len++)for(inti=1;i+le......