树剖学习笔记[待填坑]

树链剖分

树剖求LCA

树链部分
重儿子:父结点的所有儿子中子树结点数目最多的结点
轻儿子:父结点中除重儿子以外的儿子
重边:父结点和重儿子连成的边
轻边:父结点和轻儿子连成的边
重链:由多条重边连接而成的路径

  1. 整棵树会被剖分成若干条重链
  2. 轻儿子一定是每条重链的顶点
  3. 任意一条路径被切分成不超过 logn条链

$fa[u]\ udep[u]\ uson[u]\ usz[u]\ utop[u]\ $u所在重链的顶点

  1. dfs1,处理 fa, dep,son sz  O(n)
  2. dfs2,处理 top O(n)
  3. 让两个游标沿着各自的重链向上跳,跳到同一条重链上时,深度较小的那个游标所指向的点,就是LCA O(logn)

总复杂度n个节点m次查询,O(n+mlogn)

树剖求LCA
原题链接
代码

#include <iostream>
#include <vector>

using namespace std;
const int N = 5e5 + 10;
vector<int>e[N];
int fa[N], dep[N] , sz[N] ;
int son[N],top[N];
int n,m,s;

void dfs1(int u,int father)
{
	fa[u] = father , dep[u] = dep[father] + 1,sz[u] = 1;
	for (int v:e[u])
	{
		if(v == father)continue;
		dfs1(v,u);
		sz[u] += sz[v];//累加路径长
		if(sz[son[u]] < sz[v])son[u] = v;//跟换重儿子
	}
}
void dfs2(int u,int t)
{
	top[u] = t;//记录该重链链头头
	if(!son[u])return;//叶节点(无重儿子)返回
	dfs2(son[u],t);//搜重儿子
	for(int v:e[u]){
		if(v == fa[u]||v == son[u])continue;//判重
		dfs2(v,v);//搜轻儿子
	}	
}

int lca(int u,int v){
	while(top[u]!=top[v]){//上跳
		if(dep[top[u]] < dep[top[v]])swap(u,v);
		u = fa[top[u]];
	}
	return dep[u] < dep[v]?u:v;
}


int main()
{
	cin >> n  >> m  >> s ;
	for (int u,v, i = 1 ; i < n  ; i ++ )
	{
		scanf("%d%d",&u,&v);
		e[u].push_back(v);
		e[v].push_back(u);
	}
	dfs1(s,s);
	dfs2(s,s);
	for (int u,v,i = 1 ; i <= m ; i ++ )
	{
		scanf("%d%d",&u,&v);
		printf("%d\n",lca(u,v));
	}
	return 0;
}

树上修改与查询

$fa[u]\ udep[u]\ uson[u]\ usz[u]\ utop[u]\ uid[u]\ unw[u]\ $存新编号在书中所对应节点的权值

  • 树链剖分
  1. dfs1,处理 fa, dep,son sz,  O(n)
  2. dfs2,处理 top id nw  O(n)
  • 线段树维护

映射:原树序列线段树

  • 树上修改与查询

时间复杂度mlog2n

原题链接

代码

#include <iostream>
using namespace std;
#define lscc lsqq,k
#define rscc rsqq,k
#define lsqq lson,x,y
#define rsqq rson,x,y
#define lson ls,l,mid
#define rson rs,mid+1,r
#define ls p<<1
#define rs p<<1|1
#define mid (l+r>>1)
#define len (r-l+1)
const int N = 2e5 + 10;
int h[N],ne[N<<1],w[N],e[N<<1],idx;
int fa[N],dep[N],sz[N],son[N];
int top[N],id[N],nw[N],cnt,mod;
int sum[N<<2],add[N<<2],res,n,m,root;
inline int read(){
	int r=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){r=(r*10)+(ch^48);ch=getchar();} return f*r;
} 
void Add(int u,int v){e[++idx]=v,ne[idx]=h[u],h[u]=idx;}
void dfs1(int u,int father){
	fa[u]=father,dep[u]=dep[father]+1,sz[u]=1;
	for(int i=h[u];i;i=ne[i]){int v=e[i];
		if(v == father)continue;
		dfs1(v,u); sz[u] += sz[v];
		if(sz[son[u]] < sz[v])son[u]=v;
	}
}
void dfs2(int u,int t){
	top[u]=t,id[u]=++cnt,nw[cnt]=w[u];
	if(!son[u])return; dfs2(son[u],t);
	for(int i=h[u];i;i=ne[i]){ int v=e[i];
		if(v == fa[u]||v==son[u])continue;
		dfs2(v,v);
	}
}
void update(int p){sum[p]=(sum[ls]+sum[rs])%mod;}
void build(int p,int l,int r){
	if(l == r)return sum[p] = nw[l]%mod,void();
	build(lson),build(rson); update(p);
}
void pushdown(int p,int l,int r){
	add[ls]=(add[p]+add[ls])%mod;add[rs]=(add[p]+add[rs])%mod;
	sum[ls]=(sum[ls]+(add[p]*(len-(len>>1))%mod))%mod;
	sum[rs]=(sum[rs]+(add[p]*(len>>1)%mod))%mod;
	add[p] = 0;
}
int qlink(int p,int l,int r,int x,int y){
	if(x<=l&&r<=y){res=(sum[p]+res)%mod;return res;} else
	{	if(add[p])pushdown(p,l,r);
		if(x<=mid)qlink(lsqq);if(y> mid)qlink(rsqq);
	}
}
int change(int p,int l,int r,int x,int y,int k){
	if(x<=l&&r<=y){add[p]+=k,sum[p]+=k*len;} else
	{	if(add[p])pushdown(p,l,r);
		if(x<=mid)change(lscc);if(y> mid)change(rscc);
		update(p);
	}
}
int qrand(int u,int v){
	int ans=0;
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		res = 0;qlink(1,1,n,id[top[u]],id[u]);
		ans = (ans+res)%mod; u = fa[top[u]];
	}
	if(dep[u] > dep[v]) swap(u,v); 
	res=0;qlink(1,1,n,id[u],id[v]);
	ans = (res + ans) % mod;
	return ans;
}
int linkchange(int u,int v,int k){
	k %= mod;
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		change(1,1,n,id[top[u]],id[u],k);
		u = fa[top[u]];
	}
	if(dep[u]>dep[v])swap(u,v);
	change(1,1,n,id[u],id[v],k);
}
int qson(int u){res = 0;qlink(1,1,n,id[u],id[u]+sz[u]-1);return res;}
void cson(int u,int k){change(1,1,n,id[u],id[u]+sz[u]-1,k);}
signed main()
{
	n=read(),m=read(),root=read(),mod=read();
	for(int i=1;i<=n;i++)w[i]=read();
	for(int x,y,i=1;i<n;i++){
		x = read(),y = read(); 
		Add(x,y);
		Add(y,x);
	}
	dfs1(root,root);
	dfs2(root,root);
	build(1,1,n);
	for (int opt,x,y,z,i=1;i<=m;i++){
		opt = read();
		if(opt==1){
			x=read(),y=read(),z=read();
			linkchange(x,y,z);
		}
		if(opt==2){
			x=read(),y=read();
			printf("%d\n",qrand(x,y));
		}
		if(opt==3){
			x=read(),z=read();
			cson(x,z);
		}
		if(opt==4){
			x=read();
			printf("%d\n",qson(x));
		}
	}
	return 0;
}

P3178 [HAOI2015]树上操作

注意树以1为根,数据较大要开 long long(signed万岁)他与上题无差别

#include <iostream>
using namespace std;
#define lscc lsqq,k
#define rscc rsqq,k
#define lsqq lson,x,y
#define rsqq rson,x,y
#define lson ls,l,mid
#define rson rs,mid+1,r
#define ls p<<1
#define rs p<<1|1
#define mid (l+r>>1)
#define len (r-l+1)
#define int long long 
const int N = 2e5 + 10;
int h[N],ne[N<<1],w[N],e[N<<1],idx;
int fa[N],dep[N],sz[N],son[N];
int top[N],id[N],nw[N],cnt;
int sum[N<<2],add[N<<2],res,n,m;
inline int read(){
	int r=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){r=(r*10)+(ch^48);ch=getchar();} return f*r;
} 
void Add(int u,int v){e[++idx]=v,ne[idx]=h[u],h[u]=idx;}
void dfs1(int u,int father){
	fa[u]=father,dep[u]=dep[father]+1,sz[u]=1;
	for(int i=h[u];i;i=ne[i]){int v=e[i];
		if(v == father)continue;
		dfs1(v,u); sz[u] += sz[v];
		if(sz[son[u]] < sz[v])son[u]=v;
	}
}
void dfs2(int u,int t){
	top[u]=t,id[u]=++cnt,nw[cnt]=w[u];
	if(!son[u])return; dfs2(son[u],t);
	for(int i=h[u];i;i=ne[i]){ int v=e[i];
		if(v == fa[u]||v==son[u])continue;
		dfs2(v,v);
	}
}
void update(int p){sum[p]=sum[ls]+sum[rs];}
void build(int p,int l,int r){
	if(l == r)return sum[p] = nw[l],void();
	build(lson),build(rson); update(p);
}
void pushdown(int p,int l,int r){
	add[ls]+=add[p];add[rs]+=add[p];
	sum[ls]+=add[p]*(len-(len>>1));
	sum[rs]+=add[p]*(len>>1);
	add[p] = 0;
}
int qlink(int p,int l,int r,int x,int y){
	if(x<=l&&r<=y){res=sum[p]+res;return res;} else
	{	if(add[p])pushdown(p,l,r);
		if(x<=mid)qlink(lsqq);if(y> mid)qlink(rsqq);
	}
}
int change(int p,int l,int r,int x,int y,int k){
	if(x<=l&&r<=y){add[p]+=k,sum[p]+=k*len;} else
	{	if(add[p])pushdown(p,l,r);
		if(x<=mid)change(lscc);
		if(y> mid)change(rscc);
		update(p);
	}
}
void cson(int u,int k){change(1,1,n,id[u],id[u]+sz[u]-1,k);}
void add1(int u,int a){change(1,1,n,id[u],id[u],a);}
int qrand(int u,int v){
	int ans=0;
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		res = 0; qlink(1,1,n,id[top[u]],id[u]);
		ans += res; u = fa[top[u]];
	}
	if(dep[u] > dep[v]) swap(u,v); 
	res=0;qlink(1,1,n,id[u],id[v]);
	ans = res + ans;
	return ans;
}
signed main()
{
	n=read(),m=read();
	for(int i=1;i<=n;i++) w[i]=read();
	for(int x,y,i=1;i<n;i++){
		x = read(),y = read(); 
		Add(x,y);
		Add(y,x);
	}
	dfs1(1,1);
	dfs2(1,1);
	build(1,1,n);
	for (int opt,x,y,z,i=1;i<=m;i++){
		opt = read();
		if(opt==1){
			x=read(),y=read();
			add1(x,y);
		}
		if(opt==2){
			x=read(),y=read();
			cson(x,y);
		}
		if(opt==3){
			x=read();
			printf("%lld\n",qrand(1,x));
		}
	}
	return 0;
}

P2146 [NOI2015] 软件包管理器

题目大意:給出一个以1为根的树,每个节点有1或0两种状态开始时每个节点默认为0,给出n个询问。

  1. 对于install x查询从该节点x到根有多少个结点权值为0,查询后更新为1
  2. 查询uninstall x该节点x为根的子树总权值和,查询后更新为0
#include<bits/stdc++.h>
#define lson ls,l,mid
#define rson rs,mid+1,r
#define ls p<<1
#define rs p<<1|1
#define mid (l+r>>1)
using namespace std;
const int N = 2e5 + 10;
int s[N<<2],a[N<<2],sz[N],son[N],top[N],id[N],fa[N],h[N],ne[N],e[N],dep[N],idx,cnt,n,m;
char op[24];
int read(){
    int f=1,x=0;char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}return f*x;
}

void add(int u,int v){e[++idx]=v,ne[idx]=h[u],h[u]=idx;swap(u,v);e[++idx]=v,ne[idx]=h[u],h[u]=idx;}
void dfs1(int u,int fas){
    sz[u] = 1,dep[u]=dep[fas]+1,fa[u]=fas;
    for(int i=h[u];i;i=ne[i]){
    int v=e[i];if(v==fas)continue;
    dfs1(v,u); sz[u]+=sz[v];
    if(sz[son[u]]<sz[v])son[u]=v;
    }
}

void dfs2(int u,int t){
    top[u]=t,id[u]=++cnt;
    if(!son[u])return; dfs2(son[u],t);
    for(int i=h[u];i;i=ne[i]){int v=e[i];
    if(v==son[u]||v==fa[u])continue;
    dfs2(v,v);
    }
}
void pushdown(int p,int l,int r){
    a[ls]=a[rs]=a[p];if(!a[p])s[ls]=s[rs]=0;
    else s[ls]=mid-l+1,s[rs]=r-mid;a[p]=-1;
}
int get_sum(int p,int l,int r,int x,int y,int w){
    if(l>=x&&y>=r){int res=s[p]; a[p]=w; s[p]=w*(r-l+1); return res;}
    if(a[p]>=0)pushdown(p,l,r);
    int res=0;
    if(x<=mid)res += get_sum(lson,x,y,w); 
    if(y>mid)res +=get_sum(rson,x,y,w); 
    s[p]=s[ls]+s[rs];
    return res;
}
int qsum(int u,int v)
{
    int ans=0;
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        ans+=get_sum(1,1,n,id[top[u]],id[u],1);
        u=fa[top[u]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    ans+=get_sum(1,1,n,id[u],id[v],1);
    return ans;
}
int main(){
    n=read();
    for(int i=2;i<=n;i++)add(read()+1,i);
    dfs1(1,-1);
    dfs2(1,1);
    m=read();memset(s,-1,sizeof (s));
    for(int u,ans,i=1;i<=m;i++){
        scanf("%s",op);u=read()+1;
        if(*op=='i')ans=qsum(u,1),ans=dep[u]-ans;
        else ans = get_sum(1,1,n,id[u],id[u]+sz[u]-1,0);
        printf("%d\n", ans);
    }
}

posted @   Erfu  阅读(31)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示