[LOJ139]-树链剖分

题目地址

其实这个题不是个单纯的模板,所以要做单纯无脑的模板或者学习树链剖分的可以去这里【Luogu-树链剖分】,这里不会讲树链剖分的原理与实现。


开始想写一些模板题目,luogu的写得差不多了,所以就去loj看看,结果就看到了这道,emmmm

题目大意

给你一棵有点权的树,开始时根为1号点,请你实现以下操作:

  • 换根
  • 一条链上点权加
  • 一个子树内点权加
  • 询问一条链上点权的和
  • 询问一个子树内的点权和

操作大多数和luogu的模板题一样,且不用取模,但是多了一个换根操作。

所以对于链上的操作是不会影响的,只有子树部分操作有影响。

首先考虑暴力,每次换根后重构,那么复杂度显然接受不了,所以我们要考虑不重构,所以开始的时候就以一号点为根,先把树剖了,线段树建出来。

此时,我们就要考虑不同的根和子树操作该如何实现,我们假设根为 r r r,操作子树的根节点为 u u u,那么有如下几种情况(原来的子树是指的在根为1时的子树):

  • u = r u=r u=r,此时操作范围就是整个树,将整个树加或者求和即可。

bef = = = = = = = = = > =========> =========>aft

  • r r r不在 u u u的原来的子树里面时,操作范围就是原来的子树。

bef = = = = = = = = = > =========> =========>aft

  • r r r u u u的子树内时,情况就比较复杂,操作范围就为整个树减去 u u u r r r路径上深度最小的点(不包含 u u u点)的原来的子树。

bef = = = = = = = = = > =========> =========>aft

比如上面这个例子就是,我们现在6号点为根,查询2号点,它的子树就是1,3,5,也就是原来的整个树减去4号点的原来的子树剩余部分,而4号点刚好是 6 ∼ 2 6\sim 2 62上不包含2的深度最小的点。


所以我们再子树操作的时候再稍微判断一下,分类讨论求一下就好啦,求路上深度最小的点可以倍增往上跳,也可以直接树剖的线段树维护即可。

长长的代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int M=1e5+10;
const int inf=1e9;
int n,m,lg;
ll val[M];
struct ss{
	int to,last;
	ss(){}
	ss(int a,int b):to(a),last(b){}
}g[M<<1];
int head[M],cnt;
void add(int a,int b){
	g[++cnt]=ss(b,head[a]);head[a]=cnt;
	g[++cnt]=ss(a,head[b]);head[b]=cnt;
}
int dep[M],sze[M],top[M],son[M],f[M];
int num[M],rf[M],tim,root=1;
void dfs1(int a){
	sze[a]=1;
	for(int i=head[a];i;i=g[i].last){
		if(g[i].to==f[a]) continue;
		dep[g[i].to]=dep[a]+1;
		f[g[i].to]=a;
		dfs1(g[i].to);
		sze[a]+=sze[g[i].to];
		if(!son[a]||sze[son[a]]<sze[g[i].to])
		son[a]=g[i].to;
	}
}
void dfs2(int a,int b){
	top[a]=b;rf[num[a]=++tim]=a;
	if(!son[a]) return;
	dfs2(son[a],b);
	for(int i=head[a];i;i=g[i].last){
		if(g[i].to==f[a]||g[i].to==son[a]) continue;
		dfs2(g[i].to,g[i].to);
	}
} 

ll sum[M<<2],lazy[M<<2];
int minp[M<<2];
int tmin(int a,int b){
	if(a==inf)return b;
	if(b==inf)return a;
	if(dep[a]<dep[b])return a;
	else return b;
}
void pushup(int o){
	sum[o]=sum[o<<1]+sum[o<<1|1];
	minp[o]=tmin(minp[o<<1],minp[o<<1|1]);
}

void pushdown(int o,int l,int r,int mid){
	if(!lazy[o]) return;
	lazy[o<<1]+=lazy[o];
	lazy[o<<1|1]+=lazy[o];
	sum[o<<1]+=(mid-l+1)*lazy[o];
	sum[o<<1|1]+=(r-mid)*lazy[o];
	lazy[o]=0;
}

void build(int o,int l,int r){
	if(l==r){
		sum[o]=val[rf[l]];
		minp[o]=rf[l];
		return;
	}
	int mid=l+r>>1;
	build(o<<1,l,mid);
	build(o<<1|1,mid+1,r);
	pushup(o);
}

void update(int o,int l,int r,int L,int R,ll v){
	if(L<=l&&r<=R){
		sum[o]+=(r-l+1)*v;
		lazy[o]+=v;
		return;
	}
	int mid=l+r>>1;
	pushdown(o,l,r,mid);
	if(L<=mid) update(o<<1,l,mid,L,R,v);
	if(R>mid) update(o<<1|1,mid+1,r,L,R,v);
	pushup(o);
}

ll query(int o,int l,int r,int L,int R){
	if(L<=l&&r<=R) return sum[o];
	int mid=l+r>>1;
	pushdown(o,l,r,mid);
	if(R<=mid) return query(o<<1,l,mid,L,R);
	else if(L>mid) return query(o<<1|1,mid+1,r,L,R);
	else return query(o<<1,l,mid,L,R)+query(o<<1|1,mid+1,r,L,R);
}

int find(int o,int l,int r,int L,int R){
	if(R<L)return inf;
	if(L<=l&&r<=R) return minp[o];
	int mid=l+r>>1;
	if(R<=mid) return find(o<<1,l,mid,L,R);
	else if(L>mid) return find(o<<1|1,mid+1,r,L,R);
	else return tmin(find(o<<1,l,mid,L,R),find(o<<1|1,mid+1,r,L,R));
}

int lca(int a,int b){
	while(top[a]!=top[b]){
		if(dep[top[a]]<dep[top[b]])swap(a,b);
		a=f[top[a]];
	}
	if(dep[a]>dep[b])swap(a,b);
	return a;
}

int Upto(int a,int b){
	int ans=inf,tw=a;
	while(top[a]!=top[b]){
		if(dep[top[a]]<dep[top[b]])swap(a,b);
		if(top[a]!=b)ans=tmin(ans,find(1,1,n,num[top[a]],num[a]));
		else ans=tmin(ans,find(1,1,n,num[top[a]]+1,num[a]));
		a=f[top[a]];
	}
	if(dep[a]>dep[b])swap(a,b);
	ans=tmin(ans,find(1,1,n,num[a]+1,num[b]));
	return ans;
}

int check(int a){
	if(a==root) return 0;
	int L=lca(a,root);
	if(L==a) return Upto(a,root);
	else return -1;
}

void Add_Chain(int a,int b,ll w){
	while(top[a]!=top[b]){
		if(dep[top[a]]<dep[top[b]])swap(a,b);
		update(1,1,n,num[top[a]],num[a],w);
		a=f[top[a]];
	}
	if(dep[a]>dep[b])swap(a,b);
	update(1,1,n,num[a],num[b],w);
}

ll Ask_Chain(int a,int b){
	ll ans=0;
	while(top[a]!=top[b]){
		if(dep[top[a]]<dep[top[b]])swap(a,b);
		ans+=query(1,1,n,num[top[a]],num[a]);
		a=f[top[a]];
	}
	if(dep[a]>dep[b])swap(a,b);
	ans+=query(1,1,n,num[a],num[b]);
	return ans;
}

void Add_Tree(int u,ll v){
	int type=check(u);
	if(!type){
		update(1,1,n,1,n,v);
	}else if(type>0){
		update(1,1,n,1,n,v);
		update(1,1,n,num[type],num[type]+sze[type]-1,-v);
	}else{
		update(1,1,n,num[u],num[u]+sze[u]-1,v);
	}
}

ll Ask_Tree(int u){
	int type=check(u);
	if(!type){
		return query(1,1,n,1,n);
	}else if(type>0){
		ll ans=query(1,1,n,num[type],num[type]+sze[type]-1);
		return query(1,1,n,1,n)-ans;
	}else{
		return query(1,1,n,num[u],num[u]+sze[u]-1);
	}
}
int opt,a,b,c;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%lld",&val[i]);
	for(int i=2;i<=n;i++){
		scanf("%d",&a);
		add(i,a);
	}
	dfs1(1);
	dfs2(1,1);
	build(1,1,n);
	for(scanf("%d",&m);m--;){
		scanf("%d",&opt);
		if(opt==1){
			scanf("%d",&a);
			root=a;
		}else if(opt==2){
			scanf("%d%d%d",&a,&b,&c);
			Add_Chain(a,b,c);
		}else if(opt==3){
			scanf("%d%d",&a,&b);
			Add_Tree(a,b);
		}else if(opt==4){
			scanf("%d%d",&a,&b);
			printf("%lld\n",Ask_Chain(a,b));
		}else{
			scanf("%d",&a);
			printf("%lld\n",Ask_Tree(a));
		}
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

VictoryCzt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值