树链剖分模板

树链剖分:树上统计的一种优秀算法,基本思想是用将树切割成几段长链再配合数据结构转为区间问题。

我使用了线段树,需要注意的是树链剖分的额外空间极大,需要尽可能压缩

线段树的部分并无改动。

关键在于剖分的过程。

分两次dfs完成

第一遍求出每颗子树大小;每个节点的父亲节点、深度、重儿子(即子树大小最大的儿子)

第二遍按照重儿子优先的原则将节点重新编号,分成长链,这样的做法可以使每条链上的序号连续,而深搜使各子树中的节点集合也为连续区间。建立线段树

 操作:

如果操作某节点x子树上的关键值,较简单,操作序号id[x]~id[x]+size[x]-1(易证)

如果操作两点间路径上的关键值,稍复杂,每次将两点所在链分别处理直到两点位于同一链即可

下面是程序

#include
#include
#include
#define MAXN 100001
using namespace std;
int n,m,r,p;
int a[MAXN];
int h,head[MAXN];
int fa[MAXN],size[MAXN],dep[MAXN],hson[MAXN];
int id[MAXN],top[MAXN],tot;
struct Edge
{
	int v,next;
}edge[MAXN*2];
struct node
{
	int lazy,sum;
}tr[MAXN*4];
void change(int o,int nl,int nr,int l,int r,int a)
{
	if(nl==l&&nr==r)
	{
	    tr[o].lazy+=a;
		tr[o].sum+=a*(nr-nl+1);
		return;
	}
	int mid=(nl+nr)>>1;
	change(o<<1,nl,mid,nl,mid,tr[o].lazy);change(o<<1|1,mid+1,nr,mid+1,nr,tr[o].lazy);
	tr[o].lazy=0;
	if(r<=mid) change(o<<1,nl,mid,l,r,a);
	else if(l>mid) change(o<<1|1,mid+1,nr,l,r,a);
	else change(o<<1,nl,mid,l,mid,a),change(o<<1|1,mid+1,nr,mid+1,r,a);
	tr[o].sum=(tr[o<<1].sum+tr[o<<1|1].sum)%p;
}
int ask(int o,int nl,int nr,int l,int r)
{
	if(l==nl&&r==nr)
	{
		return tr[o].sum;
	}
	int mid=(nl+nr)>>1;
	change(o<<1,nl,mid,nl,mid,tr[o].lazy);change(o<<1|1,mid+1,nr,mid+1,nr,tr[o].lazy);
	tr[o].lazy=0;
	if(r<=mid) return ask(o<<1,nl,mid,l,r);
	else if(l>mid) return ask(o<<1|1,mid+1,nr,l,r);
	else return ask(o<<1,nl,mid,l,mid)+ask(o<<1|1,mid+1,nr,mid+1,r);
}
void add(int u,int v)
{
	h++;
	edge[h].v=v;
	edge[h].next=head[u];
	head[u]=h;
}
void dfs1(int last,int u)
{
	fa[u]=last;
	dep[u]=dep[last]+1;
	size[u]=1;
	int maxx=-1;
	for(int i=head[u];i;i=edge[i].next)
	{
		int v=edge[i].v;
		if(v==last) continue;
		dfs1(u,v);
		size[u]+=size[v];
		if(size[v]>maxx) maxx=size[v],hson[u]=v;
	}
}
void dfs2(int u,int topf)
{
	id[u]=++tot;
    top[u]=topf;
    change(1,1,n,tot,tot,a[u]);
    if(!hson[u]) return;
    dfs2(hson[u],topf);
    for(int i=head[u];i;i=edge[i].next)
    {
    	int v=edge[i].v;
    	if(v==fa[u]||v==hson[u]) continue;
    	dfs2(v,v);
	}
}
int ask_tree1(int x,int y)
{
	int res=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]dep[y]) swap(x,y);
	res=(res+ask(1,1,n,id[x],id[y]))%p;
	return res;
}
int ask_tree2(int x)
{
	return ask(1,1,n,id[x],id[x]+size[x]-1);
}
void change_tree1(int x,int y,int a)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]dep[y]) swap(x,y);
	change(1,1,n,id[x],id[y],a);
} 
void change_tree2(int x,int a)
{
	change(1,1,n,id[x],id[x]+size[x]-1,a);
}
int main()
{
	cin>>n>>m>>r>>p;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i

 

你可能感兴趣的:(树链剖分)