[线段树合并] 线段树合并:从入门到精通 之 入门

基操

在这里插入图片描述

T1 板/[Vani有约会]雨天的尾巴 /【模板】线段树合并

Portkey

多次给树上路径上的点加一个数,求每个节点上加得最多的数

差分,然后加回来
普通差分:维护一个值即可
现在:合并一个数组,使用线段树合并

自己查出来70行少打了一个+=✌️
就很板

#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
	int i=0,f=1;char ch=0;
	while(!isdigit(ch)&&ch!='-') ch=getchar();
	if(ch=='-') ch=getchar(),f=-1;
	while(isdigit(ch)) i=(i<<3)+(i<<1)+ch-48,ch=getchar();
	return i*f;
}

#define pb push_back
const int N=5e5+5,k=1e5;
int n,m,res[N],sz;
int dep[N],f[N][25];
vector<int>G[N];

int root[N];
struct node{
	int lch,rch,val,ans;
}tre[N*20];

void DFS(int u,int fa){
	dep[u]=dep[fa]+1;
	f[u][0]=fa;
	for(int v:G[u]) if(v^fa) DFS(v,u);
}

int lca(int u,int v){
	if(dep[u]<dep[v]) swap(u,v);
	for(int l=20;l>=0;--l)
		if(dep[f[u][l]]>=dep[v])
			u=f[u][l];
	if(u==v) return u;
	for(int l=20;l>=0;--l)
		if(f[u][l]!=f[v][l])
			u=f[u][l],v=f[v][l];
	return f[u][0];
}

void push_up(int p){
	if(tre[tre[p].lch].val<=0&&tre[tre[p].rch].val<=0) return;
	if(tre[tre[p].lch].val>=tre[tre[p].rch].val){
		tre[p].val=tre[tre[p].lch].val;
		tre[p].ans=tre[tre[p].lch].ans;
	}else{
		tre[p].val=tre[tre[p].rch].val;
		tre[p].ans=tre[tre[p].rch].ans;
	}
	return;
}

void update(int &p,int l,int r,int w,int v){
	if(!p) p=++sz;
	if(l==r){
		tre[p].val+=v;
		tre[p].ans=tre[p].val>0?l:0;
		return;
	}
	int mid=l+r>>1;
	if(w<=mid) update(tre[p].lch,l,mid,w,v);
	else update(tre[p].rch,mid+1,r,w,v);
	push_up(p);
	return;
}

int merge(int p,int rt,int l,int r){
	if(!p||!rt) return p|rt;
	if(l==r){
		tre[p].val+=tre[rt].val;
		tre[p].ans=tre[p].val>0?l:0;
		return p;
	}
	int mid=l+r>>1;
	tre[p].lch=merge(tre[p].lch,tre[rt].lch,l,mid);
	tre[p].rch=merge(tre[p].rch,tre[rt].rch,mid+1,r);
	push_up(p);
	return p;
}

void work(int u,int fa){
	for(int v:G[u]) if(v^fa){
		work(v,u);
		root[u]=merge(root[u],root[v],1,k);
	}
	res[u]=tre[root[u]].ans;
	return;
}

int main(){
	n=in,m=in;
	for(int i=1;i<n;++i){
		int u=in,v=in;
		G[u].pb(v);
		G[v].pb(u);
	}
	
	DFS(1,0);
	for(int l=1;l<=20;++l)
		for(int u=1;u<=n;++u)
			f[u][l]=f[f[u][l-1]][l-1];
	
	for(int i=1;i<=m;++i){
		int u=in,v=in,w=in,s=lca(u,v);
		update(root[u],1,k,w,1);
		update(root[v],1,k,w,1);
		update(root[s],1,k,w,-1);
		update(root[f[s][0]],1,k,w,-1);
	}
	
	work(1,0);
	
	for(int i=1;i<=n;++i)
		printf("%d\n",res[i]);
	return 0;
}

T2 天天爱跑步

Portkey

背景:这是我咕的一道题
当时学差分,还不会线段树合并,就做不了(似乎差分可以写,但是很难想)

将链拆成两段 u → l c a , l c a → v u\to lca,lca\to v ulca,lcav
发现有:
d e p u = d e p i + w i d e p u − d e p l c a + d e p i − d e p l c a = w i ⇒ d e p u − 2 d e p l c a = w i − d e p i dep_u=dep_i+w_i\\ dep_u-dep_{lca}+dep_i-dep_{lca}=w_i\Rightarrow dep_u-2dep_{lca}=w_i-dep_i depu=depi+widepudeplca+depideplca=widepu2deplca=widepi
分开看就行了
可能产生负数,故线段树值域 [ − N , N ] [-N,N] [N,N]
lca算到前一段还是后一段都无所谓,统一为前一段

42行的+=没查出来😭

#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
	int i=0,f=1;char ch=0;
	while(!isdigit(ch)&&ch!='-') ch=getchar();
	if(ch=='-') ch=getchar(),f=-1;
	while(isdigit(ch)) i=(i<<3)+(i<<1)+ch-48,ch=getchar();
	return i*f;
}

#define pb push_back
const int N=3e5+5,R=3e5,NN=N*20;
int n,m,dep[N],sz,f[N][25],tim[N],root[N];
vector<int>G[N];
int res[N],lch[NN],rch[NN],val[NN][2];

void DFS(int u,int fa){dep[u]=dep[fa]+1; f[u][0]=fa; for(int v:G[u]) if(v^fa) DFS(v,u);}

int lca(int u,int v){
	if(dep[u]<dep[v]) swap(u,v);
	for(int l=20;l>=0;--l)
		if(dep[f[u][l]]>=dep[v])
			u=f[u][l];
	if(u==v) return u;
	for(int l=20;l>=0;--l)
		if(f[u][l]!=f[v][l])
			u=f[u][l],v=f[v][l];
	return f[u][0];
}

void push_up(int p){
	if(!lch[p]&&!rch[p]) return;
	val[p][0]=val[lch[p]][0]+val[rch[p]][0];
	val[p][1]=val[lch[p]][1]+val[rch[p]][1];
	return;
}

void update(int &p,int l,int r,int w,int v,int c){
	if(!p) p=++sz;
	if(l==r){
		val[p][c]+=v;
		return;
	}
	int mid=l+r>>1;
	if(w<=mid) update(lch[p],l,mid,w,v,c);
	else update(rch[p],mid+1,r,w,v,c);
	push_up(p);
	return;
}

int merge(int p,int rt,int l,int r){
	if(!p||!rt) return p|rt;
	if(l==r){
		val[p][0]+=val[rt][0];
		val[p][1]+=val[rt][1];
		return p;
	}
	int mid=l+r>>1;
	lch[p]=merge(lch[p],lch[rt],l,mid);
	rch[p]=merge(rch[p],rch[rt],mid+1,r);
	push_up(p);
	return p;
}

int query(int p,int l,int r,int w,int c){
	if(!p) return 0;
	if(l==r) return val[p][c];
	int mid=l+r>>1;
	if(w<=mid) return query(lch[p],l,mid,w,c);
	else return query(rch[p],mid+1,r,w,c);
}

void work(int u,int fa){
	for(int v:G[u]) if(v^fa){
		work(v,u);
		root[u]=merge(root[u],root[v],-R,R);
	}
	res[u]+=query(root[u],-R,R,dep[u]+tim[u],0);
	res[u]+=query(root[u],-R,R,tim[u]-dep[u],1);
	return;
}

int main(){
	n=in,m=in;
	for(int i=1;i<n;++i){
		int u=in,v=in;
		G[u].pb(v);
		G[v].pb(u);
	}
	
	DFS(1,0);
	for(int l=1;l<=20;++l)
		for(int u=1;u<=n;++u)
			f[u][l]=f[f[u][l-1]][l-1];
	
	for(int i=1;i<=n;++i) tim[i]=in;
	for(int i=1;i<=m;++i){
		int s=in,t=in,l=lca(s,t);
		update(root[s],-R,R,dep[s],1,0);
		update(root[f[l][0]],-R,R,dep[s],-1,0);
		update(root[t],-R,R,dep[s]-2*dep[l],1,1);
		update(root[l],-R,R,dep[s]-2*dep[l],-1,1);
	}
	
	work(1,0);
	for(int i=1;i<=n;++i)
		printf("%d ",res[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值