这个树剖的代码写的我好绝望啊……两棵树傻傻搞不清。
写了两天的树剖
我脑子都快被剖开来了!!!
(超级想要引用这句话的,充分体现了我的绝望)
好,怒吼完毕,接下来来讲一下代码。(现在想想搞树剖也许是一个错误的决定,要不是因为某天天爱跑步我才不会来搞树剖呢)
其实这个树剖把……思想是挺简单的,但是主要是刚开始不知道思想的话看代码会一塌糊涂,所以先在这里把总体思想讲一下。
为了防止部分读者还不是很明白,这里给出几个名词的介绍
重儿子:指对于节点B的所有子节点中,若有x为B的子节点并且以x为根节点的子树的节点树在所有子节点中最多,则称x为B的重儿子。
轻儿子:不是重儿子的就是轻儿子。
重链:所有节点都是重儿子的就是重链。
轻链:所有节点都是轻儿子的就是轻链。
接下来我们讲思想。我们假设题目给出了一棵树A,然后有询问两点间的距离,以及将两点间最短路径的值进行修改(增加或减少)。我们可以先将重链轻链求出来,并且给A的每一个节点赋予一个新的编号,使得每一条重链上的节点的编号是连续的。这样子之后,我们就将一棵树A变成了一条链A,然后我们再造一颗线段树B,来维护A。对于每一次修改或者询问操作,我们都可以把它拆成一条条链,然后由于每一条链的编号都是连续的,就可以用B来进行区间操作,使得复杂度变低(虽然我不知道树剖的复杂度到底是什么)
#include<bits/stdc++.h> using namespace std; const int N=10000001; //这里先声明一下,我们称树A原来的编码叫做编码A,后来我们重新赋值的编码称为编码B int son[N],deep[N],siz[N],id[N],val[N],top[N],fa[N],jump[N]={0},n,num; //son[i]表示i(编码A)节点的重儿子的节点编号(编码A) //deep[i]表示i(编码A)节点的深度 //siz[i]表示以i(编码A)为节点的子树的节点个数 //id[i]表示节点i(编码A)重新编号后的编号(编号B) //val[i]表示节点i(编码B)的值 //top[i]表示节点i(编码A)这条重链的顶端节点编号(编码A) //fa[i]表示节点i(编码A)的父节点的编号(编码A) //jump是邻接表 int sum=0; struct kob{ int sta,ed,jump,val; }a[2*N]; //这个是邻接表存储的边 struct nob{ int fa,son,val; }edge[N]; //这个是刚开始存储的边 struct tre{ int l,r,val,mark; }t[4*N]; //这个是树B void pushdown(int root){ t[root].val+=t[root].mark*(t[root].r-t[root].l+1); t[root<<1].mark+=t[root].mark; t[root<<1|1].mark+=t[root].mark; t[root].mark=0; } //树B的lazy标记 void add(int sta,int ed,int val){ sum++; a[sum].sta=sta; a[sum].ed=ed; a[sum].jump=jump[sta]; jump[sta]=sum; } void dfs1(int root,int fath,int deepp){ deep[root]=deepp; siz[root]=1; son[root]=0; fa[root]=fath; for (int i=jump[root]; i; i=a[i].jump){ int pos=a[i].ed; if (pos==fath || pos==son[root]) continue; dfs1(pos,root,deepp+1); siz[root]+=siz[pos]; if (siz[son[root]]<siz[pos]) son[root]=pos; } } void dfs2(int root,int tp){ top[root]=tp; id[root]=++num; if (son[root]) dfs2(son[root],tp); for (int i=jump[root]; i; i=a[i].jump){ int pos=a[i].ed; if (pos==fa[root] || pos==son[root]) continue; dfs2(pos,pos); } } void Build(int root,int l,int r){ t[root].l=l; t[root].r=r; if (l==r){ t[root].val=val[l]; return ; } int mid=(l+r)>>1; Build(root<<1,l,mid); Build(root<<1|1,mid+1,r); t[root].val=t[root<<1].val+t[root<<1|1].val; } int find(int root,int le,int ri){ if (t[root].mark) pushdown(root); if (le<=t[root].l && t[root].r<=ri){ return t[root].val; } int mid=(t[root].l+t[root].r)>>1; if (ri<=mid) return find(root<<1,le,ri); else if (le>mid) return find(root<<1|1,le,ri); else return find(root<<1,le,mid)+find(root<<1|1,mid+1,ri); } //find函数表示在树B上查询A的一段区间值 int search(int root,int down,int up){ int fu=top[up],fd=top[down],rem=0; while (fu!=fd){ if (deep[fu]>deep[fd]){ swap(fu,fd); swap(up,down); } rem+=find(1,id[fd],id[down]); down=fa[fd]; fd=top[down]; } if (down==up) return rem;//由于边是存储在他的子节点上的,所以这个时候应当返回 if (deep[up]>deep[down]) swap(up,down); rem+=find(1,id[son[up]],id[down]); return rem; } //将两点中深度top较小的往上跳,然后每次跳一条链,由于跳的是一条链 //所以可以直接在树B上修改一段区间,当两个点的top相同时,两点肯定在一条链上 //所以可以直接修改两点之间的部分 void ch(int root,int le,int ri,int val){ if (t[root].mark) pushdown(root); if (le<=t[root].l && t[root].r<=ri){ t[root].mark+=val; pushdown(root); return ; } int mid=(t[root].l+t[root].r)>>1; if (ri<=mid) ch(root<<1,le,ri,val); else if (le>mid) ch(root<<1|1,le,ri,val); else{ ch(root<<1,le,mid,val); ch(root<<1|1,mid+1,ri,val); } t[root].val=t[root<<1].val+t[root<<1|1].val; } void change(int root,int down,int up,int val){ int fu=top[up],fd=top[down]; while (fu!=fd){ if (deep[fu]>deep[fd]){ swap(fu,fd); swap(up,down); } ch(1,id[fd],id[down],val); down=fa[fd]; fd=top[down]; } if (down==up) return ; if (deep[up]>deep[down]) swap(up,down); ch(1,id[son[up]],id[down],val); } //修改时也同理 int main(){ scanf("%d",&n); for (int i=1; i<n; i++){ scanf("%d%d%d",&edge[i].fa,&edge[i].son,&edge[i].val); add(edge[i].fa,edge[i].son,edge[i].val); add(edge[i].son,edge[i].fa,edge[i].val); } num=0; dfs1(1,0,1); //dfs1将树A上的deep,siz,son,fa数组求出 dfs2(1,1); //dfs2求出top以及为A重新编码,产生编码B for (int i=1; i<n; i++){ if (deep[edge[i].fa]>deep[edge[i].son]) swap(edge[i].fa,edge[i].son); val[id[edge[i].son]]=edge[i].val; } //将edge的边的父节点和子节点确定,同时求出val数组 Build(1,1,num); //建树B string s; while(cin>>s && s[0]!='D'){ int x,y,z; scanf("%d%d",&x,&y); if (s=="Question") printf("%d ",search(1,x,y)); else if (s=="Change"){ scanf("%d",&z); change(1,x,y,z); } } return 0; }