树链剖分就是将树分割成多条链,然后利用数据结构(线段树、树状数组等)来维护这些链。
首先就是一些必须知道的概念:
重结点:子树结点数目最多的结点;
轻节点:父亲节点中除了重结点以外的结点;
重边:父亲结点和重结点连成的边;
轻边:父亲节点和轻节点连成的边;
重链:由多条重边连接而成的路径;
轻链:由多条轻边连接而成的路径;
比如上面这幅图中,用黑线连接的结点都是重结点,其余均是轻结点,2-11、1-11就是重链,其他就是轻链,用红点标记的就是该结点所在链的起点,也就是我们?提到的top结点,还有每条边的值其实是进行dfs时的执行序号。
数组:
int val[N];//树上点的权值
int siz[N];//以x为根的子节点数
int dep[N];//该点在树上的深度
int fa[N];//他的爸爸是谁
int top[N];//该点所在的重链的深度最浅的节点
int dfn[N];//dfs序 (重链在此数组上是连续的,以一个点为根的所有子节点在此也是连续的
int id[N];//把dfn对回树上编号
int son[N];//重儿子
流程
两遍dfs确定以上数组
第一次dfs,确定dep,fa,son,siz数组。
void dfs1(int u,int fa,int dist)
{
dep[u]=dist;
siz[u]=1;
faz[u]=fa;
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].v;
if(v==fa) continue;
dfs1(v,u,dist+1);
siz[u]+=siz[v];
if(son[u]==-1||siz[v]>siz[son[u]]) son[u]=v;
}
}
第二次dfs确定top,dfn(dfs序,树上的一个点在线段树上的编号),tid(tid[i]线段树上区间为为i的子节点在树上的dfs序)
void dfs2(int u,int t)
{
top[u]=t;
cc++;
dfn[u]=cc;
tid[cc]=u;
if(!son[u]) return ;//如果已经是子节点了,return掉
dfs2(son[u],t);//先沿重链搜下去
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].v;
if(v!=son[u]&&v!=faz[u])//找轻链
{
dfs2(v,v);
}
}
}
可以完成的操作:
- 在x->y的路径上加K;
- 查询x->y的路径上的点权和
- 子树的和
- 子树全部加K
操作一二相当于找x->y的路径,把这条路径上所有的点求和或加一个值,我们考虑求lca,top数组可以加速求lca的过程
操作三四就更简单了,因为一棵子树的dfn值是连续的
void modify_path(int x,int y,int z)
{
int fx=top[x],fy=top[y];
while(fx!=fy)
{
if(dep[fx]>dep[fy])
{
modify(1,1,n,dfn[fx],dfn[x],z);
x=faz[fx];
}
else
{
modify(1,1,n,dfn[fy],dfn[y],z);
y=faz[fy];
}
fx=top[x];fy=top[y];
}
if(dfn[x]>dfn[y]) swap(x,y);
modify(1,1,n,dfn[x],dfn[y],z);
}
int query_path(int x,int y)
{
int res=0;
int fx=top[x],fy=top[y];
while(fx!=fy)
{
if(dep[fx]>dep[fy])
{
res=(res+query(1,1,n,dfn[fx],dfn[x]))%p;
x=faz[fx];
}
else
{
res=(res+query(1,1,n,dfn[fy],dfn[y]))%p;
y=faz[fy];
}
fx=top[x];fy=top[y];
}
if(dfn[x]>dfn[y]) swap(x,y);
res=(res+query(1,1,n,dfn[x],dfn[y]))%p;
return res;
}
思路
https://www.cnblogs.com/George1994/p/7821357.html
传送门
https://www.luogu.org/problemnew/show/P3384
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<cstdlib>
#include<ctime>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
inline int read(){
char ch=' ';int f=1;int x=0;
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int N=1e5+100;
int p,root;
int val[N];
struct node
{
int v,nxt;
}edge[N<<1];
int head[N],cnt;
void add(int u,int v)
{
cnt++;
edge[cnt].v=v;
edge[cnt].nxt=head[u];
head[u]=cnt;
}
int dep[N],siz[N],faz[N],son[N];
void dfs1(int u,int fa,int dist)
{
dep[u]=dist;
siz[u]=1;
faz[u]=fa;
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].v;
if(v==fa) continue;
dfs1(v,u,dist+1);
siz[u]+=siz[v];
if(!son[u]||siz[v]>siz[son[u]]) son[u]=v;
}
}
int top[N],dfn[N],tid[N],cc=0;
void dfs2(int u,int t)
{
top[u]=t;
cc++;
dfn[u]=cc;
tid[cc]=u;
if(!son[u]) return ;
dfs2(son[u],t);
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].v;
if(v!=son[u]&&v!=faz[u])
{
dfs2(v,v);
}
}
}
int sum[N<<2];
int tag[N<<2];
int n;
void update(int rt)
{
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int rt,int l,int r)
{
if(l==r)
{sum[rt]=val[tid[l]];return ;}
int mid=(l+r) >> 1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
update(rt);
}
void pushdown(int rt,int l,int r)
{
if(tag[rt])
{
int k=tag[rt],mid=(l+r)>>1;tag[rt]=0;
sum[rt<<1]=(sum[rt<<1]+(mid-l+1)*k)%p;
tag[rt<<1]=(tag[rt<<1]+k)%p;
sum[rt<<1|1]=(sum[rt<<1|1]+(r-mid)*k)%p;
tag[rt<<1|1]=(tag[rt<<1|1]+k)%p;
}
}
void modify(int rt,int l,int r,int x,int y,int z)
{
if(l>=x&&r<=y)
{
sum[rt]=(sum[rt]+(r-l+1)*z)%p;
tag[rt]=(tag[rt]+z)%p;
return ;
}
pushdown(rt,l,r);
int mid=(l+r) >> 1;
if(x<=mid)
{
modify(rt<<1,l,mid,x,y,z);
}
if(y>mid)
{
modify(rt<<1|1,mid+1,r,x,y,z);
}
update(rt);
}
int query(int rt,int l,int r,int x,int y)
{
if(l>=x&&r<=y)
{
return sum[rt];
}
pushdown(rt,l,r);
int mid=(l+r) >> 1;
int res=0;
if(x<=mid)
{
res=(res+query(rt<<1,l,mid,x,y))%p;
}
if(y>mid)
{
res=(res+query(rt<<1|1,mid+1,r,x,y))%p;
}
return res;
}
void modify_path(int x,int y,int z)
{
int fx=top[x],fy=top[y];
while(fx!=fy)
{
if(dep[fx]>dep[fy])
{
modify(1,1,n,dfn[fx],dfn[x],z);
x=faz[fx];
}
else
{
modify(1,1,n,dfn[fy],dfn[y],z);
y=faz[fy];
}
fx=top[x];fy=top[y];
}
if(dfn[x]>dfn[y]) swap(x,y);
modify(1,1,n,dfn[x],dfn[y],z);
}
int query_path(int x,int y)
{
int res=0;
int fx=top[x],fy=top[y];
while(fx!=fy)
{
if(dep[fx]>dep[fy])
{
res=(res+query(1,1,n,dfn[fx],dfn[x]))%p;
x=faz[fx];
}
else
{
res=(res+query(1,1,n,dfn[fy],dfn[y]))%p;
y=faz[fy];
}
fx=top[x];fy=top[y];
}
if(dfn[x]>dfn[y]) swap(x,y);
res=(res+query(1,1,n,dfn[x],dfn[y]))%p;
return res;
}
int main()
{
int m;
n=read();m=read();root=read();p=read();
int i,j;
for(i=1;i<=n;i++) val[i]=read();
int u,v;
for(i=1;i<n;i++){u=read();v=read();add(u,v);add(v,u);}
dfs1(root,0,1);
dfs2(root,root);
build(1,1,n);
int op,x,y,z;
for(i=1;i<=m;i++)
{
op=read();
if(op==1)
{
x=read();y=read();z=read();
modify_path(x,y,z);
}
else if(op==2)
{
x=read();y=read();
printf("%d\n",query_path(x,y));
}
else if(op==3)
{
int x=read(),z=read();
modify(1,1,n,dfn[x],dfn[x]+siz[x]-1,z);
}
else
{
int x=read();
printf("%d\n",query(1,1,n,dfn[x],dfn[x]+siz[x]-1));
}
}
return 0;
}