树链剖分:树上统计的一种优秀算法,基本思想是用将树切割成几段长链再配合数据结构转为区间问题。
我使用了线段树,需要注意的是树链剖分的额外空间极大,需要尽可能压缩
线段树的部分并无改动。
关键在于剖分的过程。
分两次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