参考和学习自:dalao's bloghtml
树链剖分,一个神奇的算法,用来解决树上的求和,找最值,修改以及许多玄学操做的算法算法
其实,上面讲的求和,找最值和修改都是像线段树同样对于序列上的操做。固然若是你用主席树,Splay等高级数据结构该能够作更多的事情数组
而树剖就是这样,它能使得那些维护序列的操做和数据结构能够被用在树上数据结构
其实也是一个预处理同样的东西了函数
首先先明确一些概念:(不懂也没事)学习
重结点:子树结点数目最多的结点;ui
轻节点:父亲节点中除了重结点之外的结点;spa
重边:父亲结点和重结点连成的边;code
轻边:父亲节点和轻节点连成的边;htm
重链:由多条重边链接而成的路径;
轻链:由多条轻边链接而成的路径;
而后咱们就能够经过一种方法把树上的节点从新编号,使得能够向维护序列同样的维护它们
咱们肯定一种标号方式,优先对重链上的点进行编号,而后再轻链
这样,咱们发现:一棵子树中,全部的点都被连续编号了,且重链上的点的标号较小
所以咱们在查询时若两个点在同一条重链上就能够直接查询(编号是连续的)
若是不是的话就把一个点(深度较深)先移到当前这条重链的顶端,而后向上经过一条轻边便可
具体看代码吧,毕竟有些东西只可意会,不可言传
这里给出一些数组的意义
dep[]全部节点的深度
father[]全部节点的直系father
size[]全部以当前节点为根的子树节点个数(包括本身)
id[]每一个节点树剖后的编号
s[]每一个节点树剖后新的值,与id相对应
son[]每一个节点的重儿子
top[]每一个节点所在的重链的链的顶端
线段树就不介绍了吧
CODE
#include<cstdio> #include<cstring> const int N=100005; typedef long long LL; using namespace std; struct segtree { LL add,sum; }tree[N<<2]; struct edge { int to,next; }e[N<<1]; int n,q,root,p,a[N],head[N],cnt,dep[N],father[N],size[N],id[N],s[N],son[N],top[N],opt,x,y,z,tot; inline char tc(void) { static char fl[100000],*A=fl,*B=fl; return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++; } inline void read(int &x) { x=0; char ch=tc(); while (ch<'0'||ch>'9') ch=tc(); while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc(); } inline void write(LL x) { if (x/10) write(x/10); putchar(x%10+'0'); } inline void add(int x,int y) { e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt; } inline void swap(int &a,int &b) { int t=a; a=b; b=t; } inline void up(int root) { tree[root].sum=(tree[root<<1].sum+tree[root<<1|1].sum)%p; } inline void down(int root,int l,int r) { if (tree[root].add) { tree[root<<1].add=(tree[root<<1].add+tree[root].add)%p; tree[root<<1|1].add=(tree[root<<1|1].add+tree[root].add)%p; tree[root<<1].sum=(tree[root<<1].sum+tree[root].add*l)%p; tree[root<<1|1].sum=(tree[root<<1|1].sum+tree[root].add*r)%p; tree[root].add=0; } } inline void build(int root,int l,int r) { if (l==r) { tree[root].sum=s[l]; return; } int mid=l+r>>1; build(root<<1,l,mid); build(root<<1|1,mid+1,r); up(root); } inline void modify(int root,int l,int r,int beg,int end,int k) { if (l>=beg&&r<=end) { tree[root].add=(tree[root].add+k)%p; tree[root].sum=(tree[root].sum+k*(r-l+1))%p; return; } int mid=l+r>>1; down(root,mid-l+1,r-mid); if (beg<=mid) modify(root<<1,l,mid,beg,end,k); if (end>mid) modify(root<<1|1,mid+1,r,beg,end,k); up(root); } inline LL query(int root,int l,int r,int beg,int end) { if (l>=beg&&r<=end) return tree[root].sum; int mid=l+r>>1; LL res=0; down(root,mid-l+1,r-mid); if (beg<=mid) res=(res+query(root<<1,l,mid,beg,end))%p; if (end>mid) res=(res+query(root<<1|1,mid+1,r,beg,end))%p; return res; } inline void DFS1(int now,int fa,int d) { father[now]=fa; dep[now]=d; size[now]=1; int res=-1; for (register int i=head[now];i!=-1;i=e[i].next) if (e[i].to!=fa) { DFS1(e[i].to,now,d+1); size[now]+=size[e[i].to]; if (size[e[i].to]>res) res=size[e[i].to],son[now]=e[i].to; } } inline void DFS2(int now,int topf) { id[now]=++tot; s[tot]=a[now]; top[now]=topf; if (!son[now]) return; DFS2(son[now],topf); for (register int i=head[now];i!=-1;i=e[i].next) if (e[i].to!=father[now]&&e[i].to!=son[now]) DFS2(e[i].to,e[i].to); } inline void change(int x,int y,int z) { while (top[x]!=top[y]) { if (dep[top[x]]<dep[top[y]]) swap(x,y); modify(1,1,n,id[top[x]],id[x],z); x=father[top[x]]; } if (dep[x]<dep[y]) swap(x,y); modify(1,1,n,id[y],id[x],z); } inline void updata(int x,int z) { modify(1,1,n,id[x],id[x]+size[x]-1,z); } inline LL get_sec(int x,int y) { LL res=0; while (top[x]!=top[y]) { if (dep[top[x]]<dep[top[y]]) swap(x,y); res=(res+query(1,1,n,id[top[x]],id[x]))%p; x=father[top[x]]; } if (dep[x]<dep[y]) swap(x,y); res=(res+query(1,1,n,id[y],id[x]))%p; return res; } inline LL get_son(int x) { return query(1,1,n,id[x],id[x]+size[x]-1); } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); register int i; memset(head,-1,sizeof(head)); memset(e,-1,sizeof(e)); read(n); read(q); read(root); read(p); for (i=1;i<=n;++i) read(a[i]); for (i=1;i<n;++i) { read(x); read(y); add(x,y); add(y,x); } DFS1(root,-1,0); DFS2(root,root); build(1,1,n); while (q--) { read(opt); read(x); if (opt==1) read(y),read(z),change(x,y,z%p); if (opt==2) read(y),write(get_sec(x,y)),putchar('\n'); if (opt==3) read(z),updata(x,z%p); if (opt==4) write(get_son(x)),putchar('\n'); } return 0; }
树剖CODE仍是比较长的
建议你们码的顺序:主程序+其余辅助函数+树剖预处理函数+线段树函数
一鼓作气最重要
还有另一道板子题Luogu P2590改一下线段树便可