首页 > 其他分享 >边权树链剖分

边权树链剖分

时间:2022-12-15 21:45:00浏览次数:62  
标签:val 剖分 cov int text 边权 树链 add

边权树链剖分

一般的树链剖分都是维护的点权,而如果要维护边权怎么办呢?

思路

我们可以把边权转换为点权。一个点有很多个儿子,但只有一个父亲。如果一个节点的点权记录到其儿子的边权就显然不行了,只能记录这个点到其父亲的边权。

如果要修改查询 \(\text{x---y}\) 的所有边权,通过维护点权的思路,先修改查询 \(\text{x---lca(x,y)}\) 再修改查询 \(\text{lca(x,y)---y}\) ,但这样会出现一个问题。

像上面这张图,如果要修改查询 \(\text{4---6}\) ,应先从 \(\text{4---3}\) ,再从 \(\text{3---6}\)。但我们发现点 \(\text{3}\) 存储的点权是 \(\text{3---1}\) 的边权,不属于要修改查询的,不应该修改。所以我们应该对点权树剖的代码做一下改动。

tree.change(1,dfn[y],dfn[x],z);
tree.change(1,dfn[y]+1,dfn[x],z);

这样就能避免上述情况了。剩下的操作都和点权树剖一样。

题目

P4315 月下“毛景树”

这个题就是边权树剖的模板,不过有一个坑点,就是要注意push_up/push_down的时候要清空各种标记。

代码

#include<bits/stdc++.h>
using namespace std;
const int MAXN=100005;
int ver[MAXN<<1],nxt[MAXN<<1],head[MAXN],edge[MAXN<<1],a[MAXN],tot,n;
int fa[MAXN],de[MAXN],dfn[MAXN],fdfn[MAXN],top[MAXN],son[MAXN],siz[MAXN],cnt;
int eg[MAXN][2];
void add(int x,int y,int z){
	ver[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
	edge[tot]=z;
}
void dfs1(int x,int f,int e){//处理各种数组
	fa[x]=f,de[x]=de[fa[x]]+1,a[x]=e,siz[x]=1;//a[x]=e:把点权设成这个点到父亲的边权
	for(int i=head[x];i;i=nxt[i]){//板子
		if(ver[i]==fa[x])continue;
		dfs1(ver[i],x,edge[i]);
		siz[x]+=siz[ver[i]];
		if(siz[ver[i]]>siz[son[x]])son[x]=ver[i];
	}
}
void dfs2(int x,int f){//板子
	top[x]=f,dfn[x]=++cnt,fdfn[cnt]=x;
	if(!son[x])return;
	dfs2(son[x],f);
	for(int i=head[x];i;i=nxt[i]){
		if(ver[i]==fa[x])continue;
		if(ver[i]==son[x])continue;
		dfs2(ver[i],ver[i]);
	}
}
struct SegmentTree{//板子
	const int NO_CHANGE=-0x7fffffff;
	struct node{
		int l,r,maxn,cov,add;
	}t[MAXN<<2];
	#define LSON (p<<1)
	#define RSON ((p<<1)|1)
	#define mid ((l+r)>>1)
	void make_lazy_tag_cov(int p,int val){
		t[p].add=0;//赋值时应当清空加法标记
		t[p].maxn=val;
		t[p].cov=val;
	}
	void make_lazy_tag_add(int p,int val){
		t[p].maxn+=val;//加法时不用清空赋值标记
		t[p].add+=val;
	}
	void push_up(int p){
		t[p].maxn=max(t[LSON].maxn,t[RSON].maxn);
	}
	void push_down(int p){//QAQ,线段树都敲烂了,还是错了push_down. 
		if(t[p].cov!=NO_CHANGE){//下传赋值标记
			make_lazy_tag_cov(LSON,t[p].cov);
			make_lazy_tag_cov(RSON,t[p].cov);
			t[p].cov=NO_CHANGE;
		}
		if(t[p].add){//下穿加法标记
			make_lazy_tag_add(LSON,t[p].add);
			make_lazy_tag_add(RSON,t[p].add);
			t[p].add=0;
		}
	}
	void build(int p,int l,int r){//板子
		if(l>r)return;
		t[p].l=l,t[p].r=r,t[p].add=0,t[p].cov=NO_CHANGE;
		if(l==r){
			t[p].maxn=a[fdfn[l]];
			return;
		}
		build(LSON,l,mid);
		build(RSON,mid+1,r);
		push_up(p);
	}
	void change_cov(int p,int l,int r,int val){//板子
		if(l>r)return;
		if(l<=t[p].l&&t[p].r<=r){
			make_lazy_tag_cov(p,val);
			return;
		}
		push_down(p);
		if(t[LSON].r>=l)change_cov(LSON,l,r,val);
		if(t[RSON].l<=r)change_cov(RSON,l,r,val);
		push_up(p);
	}
	void change_add(int p,int l,int r,int val){//板子
		if(l>r)return;
		if(l<=t[p].l&&t[p].r<=r){
			make_lazy_tag_add(p,val);
			return; 
		}
		push_down(p);
		if(t[LSON].r>=l)change_add(LSON,l,r,val);
		if(t[RSON].l<=r)change_add(RSON,l,r,val);
		push_up(p);
	}
	int ask_max(int p,int l,int r){//板子
		if(l>r)return -0x7fffffff;
		if(l<=t[p].l&&t[p].r<=r)return t[p].maxn;
		push_down(p);
		int ans=-0x7fffffff;
		if(t[LSON].r>=l)ans=max(ans,ask_max(LSON,l,r));
		if(t[RSON].l<=r)ans=max(ans,ask_max(RSON,l,r));
		return ans;
	} 
}tree;
void cover_u_to_v(int u,int v,int w){
	while(top[u]!=top[v]){//板子
		if(de[top[u]]<de[top[v]])swap(u,v);
		tree.change_cov(1,dfn[top[u]],dfn[u],w);
		u=fa[top[u]];
	}
	if(de[u]<de[v])swap(u,v);
	tree.change_cov(1,dfn[v]+1,dfn[u],w);//与点权树剖不一样的地方
}
void add_u_to_v(int u,int v,int w){
	while(top[u]!=top[v]){//板子
		if(de[top[u]]<de[top[v]])swap(u,v);
		tree.change_add(1,dfn[top[u]],dfn[u],w);
		u=fa[top[u]];
	}
	if(de[u]<de[v])swap(u,v);
	tree.change_add(1,dfn[v]+1,dfn[u],w);//与点权树剖不一样的地方
}
int ask_max_u_to_v(int u,int v){
	int ans=-0x7fffffff;
	while(top[u]!=top[v]){//板子
		if(de[top[u]]<de[top[v]])swap(u,v);
		ans=max(ans,tree.ask_max(1,dfn[top[u]],dfn[u]));
		u=fa[top[u]];
	}
	if(de[u]<de[v])swap(u,v);
	ans=max(ans,tree.ask_max(1,dfn[v]+1,dfn[u]));//与点权树剖不一样的地方
	return ans;
}
int main(){
	cin>>n;
	for(int i=1,w;i<n;i++){
		cin>>eg[i][0]>>eg[i][1]>>w;
		add(eg[i][0],eg[i][1],w);
		add(eg[i][1],eg[i][0],w); 
	}
	fa[1]=de[1]=1;
	dfs1(1,1,0);
	dfs2(1,1);
	tree.build(1,1,n);
	string op;
	for(int i=1,u,v,w;;i++){
		cin>>op;
		if(op=="Stop")break;
		if(op=="Max"){
			cin>>u>>v;
			cout<<ask_max_u_to_v(u,v)<<endl;
		}
		if(op=="Cover"){
			cin>>u>>v>>w;
			cover_u_to_v(u,v,w);
		}
		if(op=="Add"){
			cin>>u>>v>>w;
			add_u_to_v(u,v,w);
		}
		if(op=="Change"){
			cin>>u>>w;
			cover_u_to_v(eg[u][0],eg[u][1],w);
		}
	}
	return 0;
}

标签:val,剖分,cov,int,text,边权,树链,add
From: https://www.cnblogs.com/maniubi/p/16986073.html

相关文章

  • 重链剖分学习笔记
    最近在机房自习复习了一下这些东西,主要给自己看的。参考文献:OIWiki《算法竞赛》(罗勇军,郭卫斌著)1重链剖分简介1.1重链剖分基本性质树链剖分,就是把树分成若......
  • 重链剖分学习笔记
    最近在机房自习复习了一下这些东西,主要给自己看的。参考文献:OIWiki《算法竞赛》(罗勇军,郭卫斌著)1重链剖分简介1.1重链剖分基本性质树链剖分,就是把树分成若......
  • 树链剖分学习笔记
    树链剖分学习笔记简介树链剖分是一种可以把树丢到线段树上维护的一种算法,时间复杂度为\(O(n\log^2n)\)。思路一、一些概念1.重儿子:如果一个点有儿子,那么所有儿子中......
  • P3128 [USACO15DEC]Max Flow P(树上倍增和树链剖分)
    思路1(树上倍增$+$树上差分)每次都修改一条从\(u\)到\(v\),不就是树上差分的专门操作吗??直接用倍增求\(LCA\),每次\(d[u]++,d[v]++,d[LCA(u,v)]--,d[f[LCA(u,v)][0]]--\)。......
  • 浅谈树链剖分
    树链剖分定理重儿子:一个节点所有儿子中,子树大小最大的儿子即为重儿子,如有多个,任取一个即可。轻儿子:除了重儿子外的所有儿子。重边:父节点\(\to\)重儿子的边。重链:由......
  • 树链剖分
    树链剖分0x00绪言在阅读这篇文章前,确保你已学会你下内容:线段树深度优先遍历会了这些就可以开始阅读本篇文章了。0x01什么是树剖把一棵树拆成若干个不相交的链,然......
  • 浅析树链剖分
    可以看出树链剖分的作用就是将一棵树变成一个可处理的dfs序,树上操作一般由​​线段树​​来维护,看一下模板题​​BZOJ1036​​和​​POJ3237​​。......
  • 树链剖分学习笔记(补发)
    求大佬指错QaQ个人推荐的题单:树链剖分练习题个人感觉树链剖分就是把树上的节点按照某种顺序重新编号一次以便于用线段树、树状数组等维护。这次讲讲轻重链剖分。模板题......
  • 【LGR125D】【JRKSJ R5】Concvssion(多项式,长链剖分)
    Sub1:\(a_i=(i+1)\bmodn\)即图只有一个环。设\(g_u\)表示原来\(u\)上有多少个点,\(f_u=u\)表示\(u\)的点权。那么对于某个\(k\in[1,n]\),\(ans_k=\sum_{u}g_uf_......
  • 浅谈离线树链信息的一种并查集做法
    出处problem一棵树,点上有一些满足结合律的信息,\(m\)次询问求出一条链上的点权之“和”,允许离线。\(n,m\leq10^5\)。solution......