树链剖分剖后感

来源:互联网 发布:java 获取当天日期 编辑:程序博客网 时间:2024/06/04 00:07

1、我的脑子要被剖开啦。
2、辣鸡教材!
3、抱紧大佬博客才是最好的!
当然了,思想是在教材上看的,但是教材上一条重链一个线段树真的是……emmmm
然后就参考了正确的入门姿势,成功过了洛谷上的模板题。
树链剖分,就是将树拆成一小段一小段连续的链,得到线性的数据,但这个线性的数据使用是有限制的,比如跨段查询或修改的时候就会出错,那就只能一段一段处理,然后再合并。这时要尽量使每段更长些,那么就可以将任务尽可能多的交给线段树处理,这样复杂度是log级别的。
通过两次dfs将整颗树分成轻链和重链(同一重链在线段树上是连续的),这样将树拆成一段一段连续的链然后放到线段树上去,当查询两个节点之间的东西时,通过top[]数组和fa[]数组将这两个节点向上挪动,每次将deep[]较深的b移到fa[top[b]]处(此过程中顺便处理top[b]->b在线段树上值),重复此过程直到两个节点的top[]相同,top[]相同就意味着此时这两个节点在同一重链上,此时再查找一次(同一重链在线段树上是连续的)。

处理以节点x为根的整个子树:用线段树处理[pos[x],pos[x]+siz[x]-1]即可
查询节点x和节点y的LCA(最近公共祖先):将两点不断向上挪动并处理,直到两点的top[]相同,deep[]小的点即是最近公共祖先
处理节点x到节点y的路径上的点:将两点不断向上挪动并处理,直到两点的top[]相同,最后用线段树处理[pos[x],pos[y]]即可
处理节点x到节点y的路径上的边:将两点不断向上挪动并处理,直到两点的top[]相同,最后用线段树处理[pos[son[x]],pos[y]]即可(用每个点表示通向其父节点的边)
以洛谷模板题为例给出代码:

#include<bits/stdc++.h>using namespace std;const int MAXN=100010;struct line{    int y,next;}l[MAXN<<1];int n,m,r,p,Left,Right,x,y,z,type,cnt=0;int fa[MAXN],deep[MAXN],son[MAXN],siz[MAXN],fi[MAXN];int top[MAXN],pos[MAXN],num=0,in[MAXN],temp[MAXN];int t[MAXN<<2],delta[MAXN<<2];int read(){    int num=0;char c=getchar();    for(;c<'0'||c>'9';c=getchar());    for(;c>='0'&&c<='9';c=getchar())        num=(num<<3)+(num<<1)+c-48;    return num;}void make_line(int x,int y){    l[++cnt].y=y;l[cnt].next=fi[x];fi[x]=cnt;    l[++cnt].y=x;l[cnt].next=fi[y];fi[y]=cnt;}void dfs(int x,int f,int d){//第一次dfs,寻找重儿子    fa[x]=f;deep[x]=d;    son[x]=0;siz[x]=1;    for(int i=fi[x];i;i=l[i].next){        int v=l[i].y;        if(v==f)continue;        dfs(v,x,d+1);        siz[x]+=siz[v];        if(siz[v]>siz[son[x]])son[x]=v;    }}void dfs2(int x,int tp){//第二次dfs,构造重链,记录节点在线段树上的位置    top[x]=tp;pos[x]=++num;    if(son[x])dfs2(son[x],tp);    for(int i=fi[x];i;i=l[i].next){        int v=l[i].y;        if(v==fa[x]||v==son[x])continue;        dfs2(v,v);    }}void make_tree(int l,int r,int root){//初始化    if(l==r){        t[root]=in[l];        return;    }    int mid=(l+r)>>1;    make_tree(l,mid,root<<1);    make_tree(mid+1,r,root<<1|1);    t[root]=t[root<<1]+t[root<<1|1];}void push_down(int l,int r,int root){//下移delta    int mid=(l+r)>>1;    delta[root<<1]+=delta[root];delta[root<<1|1]+=delta[root];    delta[root<<1]%=p;delta[root<<1|1]%=p;    t[root<<1]=(t[root<<1]+delta[root]*(mid-l+1))%p;    t[root<<1|1]=(t[root<<1|1]+delta[root]*(r-mid))%p;    delta[root]=0;}void add(int l,int r,int root){//线段树模板    if(r<Left||l>Right)return;    if(Left<=l&&Right>=r){        delta[root]+=z;        delta[root]%=p;        t[root]=(t[root]+z*(r-l+1))%p;        return;    }    int mid=(l+r)>>1;    push_down(l,r,root);    add(l,mid,root<<1);    add(mid+1,r,root<<1|1);    t[root]=t[root<<1]+t[root<<1|1];    return;}long long find(int l,int r,int root){//模板<<1    if(r<Left||l>Right)return 0;    if(Left<=l&&Right>=r)return t[root];    int mid=(l+r)>>1;    push_down(l,r,root);    return (find(l,mid,root<<1)+find(mid+1,r,root<<1|1))%p;}void change(){    int a=read(),b=read();z=read();    int ta=top[a],tb=top[b];    while(ta!=tb){        if(deep[ta]>deep[tb]){            swap(ta,tb);            swap(a,b);        }        Left=pos[tb];Right=pos[b];        add(1,n,1);//分段处理        b=fa[tb];tb=top[b];    }    if(deep[a]>deep[b])swap(a,b);    Left=pos[a];    Right=pos[b];    add(1,n,1);//同一重链后的处理}void query(){    int a=read(),b=read();    int ta=top[a],tb=top[b];    long long ans=0;    while(ta!=tb){        if(deep[ta]>deep[tb]){            swap(ta,tb);            swap(a,b);        }        Left=pos[tb];Right=pos[b];        ans+=find(1,n,1);//同上        ans%=p;        b=fa[tb];tb=top[b];    }    if(deep[a]>deep[b])swap(a,b);    Left=pos[a];    Right=pos[b];    ans+=find(1,n,1);    ans%=p;    printf("%lld\n",ans);}void work(){    n=read();m=read();r=read();p=read();    for(int i=1;i<=n;++i)        temp[i]=read();    for(int i=1;i<n;++i){        x=read();y=read();        make_line(x,y);    }    dfs(r,r,1);    dfs2(r,r);    for(int i=1;i<=n;++i)        in[pos[i]]=temp[i];    make_tree(1,n,1);    for(int i=1;i<=m;++i){        type=read();        if(type==1)change();        if(type==2)query();        if(type==3){            x=read(),z=read();            Left=pos[x];Right=Left+siz[x]-1;//注意子树的操作            add(1,n,1);        }        if(type==4){            x=read();            Left=pos[x];Right=Left+siz[x]-1;            printf("%lld\n",find(1,n,1));        }    }}int main(){    work();    return 0;}

模板先到这里,还有很多题要做……