码迷,mamicode.com
首页 > 其他好文 > 详细

并不对劲的树链剖分

时间:2018-01-28 11:14:16      阅读:169      评论:0      收藏:0      [点我收藏+]

标签:bubuko   span   维护   git   ima   lap   body   eof   地方   

听上去像是熟练剖粪。

一棵树可以看成是很多条链组成的。那么把这些链拼成一条线,在树上进行区间操作时就可以将每次操作的部分拆成很多段连续的部分。这样就可以用线段树什么的维护了。那么问题又来了,如果瞎拆的话,可能会有一次操作涉及的链很倒霉,每一个点都被断成了一部分。这样就不得不用链的长度的时间复杂度来执行此次操作。能不能让每条路径被断的次数小一些呢?想必是能的,这要用到轻重链剖分。

重链上的点在线段树上是一段连续是空间,所以要使每条路径上的点少一些。大致想一个贪心策略,可以尽量往点多的地方分重链。这样的话,从点u出发被分到轻链的子树大小肯定不超过u的子树大小的一般。每次走轻链都会使得子树大小变为原来的一半或更小,路径上的轻链就是log n级别的了。

这样照着子树大小将树分为重链和轻链,从根往下走,每次先走重链,按照dfs序列就可以将树拉成一条链了。注意记录一下对于每个点x,top(x)表示x所在重链的深度最浅的点。

该如何区间操作呢?会发现,对一条路径操作时,可以将路径分为很多条重链。

技术分享图片

 

对于如图所示的一棵树,假设要对15到14的路径操作,可以先操作14,15到各自的top这一段,也就是15到20,14到8。然后找到20,8各自的父亲。这时两个点在同一条重链上,直接修改它们之间的部分就行了。也就是说,对于两个点x,y,要想对它们之间的路径进行操作,就可以操作它们到top(x),top(y)的一段。然后再走到top(x),top(y)的父亲,再重复这个过程。需要注意的是,每次应该走top最深的点,以防两个点错过了。

令人高兴的是,某点的子树在dfs序列中肯定是连续的一段,所以可以直接进行子树操作。

令人头疼的是,这样一来,线段树上支持的所有区间操作都可以在树上做了。

技术分享图片
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define maxn 100010
#define LL long long 
using namespace std;
LL n,m,r;
LL fir[maxn],nxt[maxn*2],v[maxn*2],s[maxn],cnt;
LL siz[maxn],fa[maxn],dep[maxn],top[maxn],son[maxn],mp[maxn],tim=0;
LL read()
{
    LL xx=0,ff=1;
    char ch=getchar();
    while(isdigit(ch)==0 && ch!=-)ch=getchar();
    if(ch==-)ff=-1,ch=getchar();
    while(isdigit(ch))xx=xx*10+ch-0,ch=getchar();
    return xx*ff;
}
LL tre[maxn*4],mark[maxn*4],p,a[maxn];
void build(LL node,LL li,LL ri)
{
    if(li==ri)
    {
        tre[node]=a[li]%p;
        return;
    }
    LL mid=(li+ri)>>1;
    build((node<<1),li,mid);
    build((node<<1)+1,mid+1,ri);
    tre[node]=(tre[(node<<1)]+tre[(node<<1)+1])%p;
}
LL add(LL node,LL li,LL ri,LL xi,LL yi,LL k)
{
    if(li>=xi&&ri<=yi)
     {
        mark[node]+=k;
        return (k*(ri-li+1))%p;
    }
    if(ri<xi||li>yi)
        return 0;
    LL mid=(li+ri)>>1;
    LL tmp1=add((node<<1),li,mid,xi,yi,k);
    LL tmp2=add((node<<1)+1,mid+1,ri,xi,yi,k);
    tre[node]+=tmp1+tmp2;
    tre[node]%=p;
    return (tmp1+tmp2)%p;
}
LL ask(LL node,LL li,LL ri,LL xi,LL yi,LL tmp)
{
    if(li>=xi&&ri<=yi)
    {
        return (tre[node]+tmp*(ri-li+1))%p;
    }
    if(ri<xi||li>yi)return 0;
    LL mid=(li+ri)>>1;
    return (ask((node<<1),li,mid,xi,yi,tmp+mark[(node<<1)])+ask((node<<1)+1,mid+1,ri,xi,yi,tmp+mark[(node<<1)+1]))%p;
}
void getson(LL u)
{
    siz[u]=1;LL maxson=0;
    for(LL k=fir[u];k!=-1;k=nxt[k])
    {
        LL vv=v[k];
        if(vv!=fa[u])
        {
            fa[vv]=u,dep[vv]=dep[u]+1;
            getson(vv);
            maxson= siz[maxson]<siz[vv] ? vv : maxson;
            siz[u]+=siz[vv];
        }
    }
    son[u]=maxson;
}
void gettop(LL u,LL anc)
{
    mp[u]=++tim, a[tim]=s[u],top[u]=anc;
    if(son[u]!=0)gettop(son[u],anc);
    for(LL k=fir[u];k!=-1;k=nxt[k])
    {
        LL vv=v[k];
        if(vv!=fa[u] && vv!=son[u])
            gettop(vv,vv);
    }
}
void get_num()
{
    LL x=read(),y=read(),tx=top[x],ty=top[y],ans=0;
    while(tx!=ty)
    {
        if(dep[ty]>dep[tx])swap(x,y),swap(tx,ty);
        ans+= ask(1,1,n,mp[tx],mp[x],mark[1]);
        x=fa[tx],tx=top[x];
    }
    ans+=ask(1,1,n,min(mp[x],mp[y]),max(mp[x],mp[y]),23658756-23658756+mark[1]);
    printf("%lld\n",ans%p);
}
void add_num()
{
    LL x=read(),y=read(),ad=read(),tx=top[x],ty=top[y],ans=0;
    while(tx!=ty)
    {
        if(dep[ty]>dep[tx])swap(x,y),swap(tx,ty);
         add(1,1,n,mp[tx],mp[x],ad);
        x=fa[tx],tx=top[x];
    }
    /*while(top[x]!=top[y])
    {
        if(deep[top[x]]<deep[top[y]])swap(x,y);
        add(1,1,n,mp[top[x]],top[x],ad);
        x=fa[top[x]];
    }*/
     add(1,1,n,min(mp[x],mp[y]),max(mp[x],mp[y]),ad);
}
void get_kid_num()
{
    LL x=read(),ans=ask(1,1,n,mp[x],mp[x]+siz[x]-1,0+mark[1]);
    printf("%lld\n",ans%p);
}
void add_kid_num()
{
    LL x=read(),ad=read();
     add(1,1,n,mp[x],mp[x]+siz[x]-1,ad);
}
int main()
{
    n=read(),m=read(),r=read(),p=read();
    for(LL i=1;i<=n;i++)
    {
       s[i]=read();
    }
    memset(fir,-1,sizeof(fir));
    for(LL i=1;i<n;i++)
    {
        LL u=read();
        v[++cnt]=read();
        nxt[cnt]=fir[u];
        fir[u]=cnt;
        v[++cnt]=u,u=v[cnt-1]; 
        nxt[cnt]=fir[u];
        fir[u]=cnt;
    }
    dep[r]=1;
    getson(r);
    gettop(r,r);
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        LL f=read();
        if(f==1)add_num();
        if(f==2)get_num();
        if(f==3)add_kid_num();
        if(f==4)get_kid_num();
    }
    return 0;
}
邪教线段树版
技术分享图片
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define maxn 100010
#define Philosophy long long
using namespace std;
Philosophy change,the_,boss,of[maxn*2],this_[maxn],gym[maxn*2];
Philosophy slaves[maxn],get[maxn],your[maxn],ass[maxn],back[maxn],here[maxn];
Philosophy billy[maxn*4],herrington[maxn*4];
Philosophy ass_[maxn],we,can;
Philosophy philosophy()
{
    Philosophy xx=0,ff=1;
    char ch=getchar();
    while(isdigit(ch)==0 && ch!=-)ch=getchar();
    if(ch==-)ff=-1,ch=getchar();
    while(isdigit(ch))xx=xx*10+ch-0,ch=getchar();
    return xx*ff;
}
Philosophy mark,wolves[maxn];
void boy(Philosophy node,Philosophy li,Philosophy ri)
{
    if(li==ri)
    {
        billy[node]=wolves[li]%mark;
        return;
    }
    Philosophy mid=(li+ri)>>1;
    boy((node<<1),li,mid);
    boy((node<<1)+1,mid+1,ri);
    billy[node]=(billy[(node<<1)]+billy[(node<<1)+1])%mark;
}
Philosophy next(Philosophy node,Philosophy li,Philosophy ri,Philosophy xi,Philosophy yi,Philosophy k)
{
    if(li>=xi&&ri<=yi)
     {
        herrington[node]+=k;
        return (k*(ri-li+1))%mark;
    }
    if(ri<xi||li>yi)
        return 0;
    Philosophy mid=(li+ri)>>1;
    Philosophy tmp1=next((node<<1),li,mid,xi,yi,k);
    Philosophy tmp2=next((node<<1)+1,mid+1,ri,xi,yi,k);
    billy[node]+=tmp1+tmp2;
    billy[node]%=mark;
    return (tmp1+tmp2)%mark;
}
Philosophy door(Philosophy node,Philosophy li,Philosophy ri,Philosophy xi,Philosophy yi,Philosophy tmp)
{
    if(li>=xi&&ri<=yi)
    {
        return (billy[node]+tmp*(ri-li+1))%mark;
    }
    if(ri<xi||li>yi)return 0;
    Philosophy mid=(li+ri)>>1;
    return (door((node<<1),li,mid,xi,yi,tmp+herrington[(node<<1)])+door((node<<1)+1,mid+1,ri,xi,yi,tmp+herrington[(node<<1)+1]))%mark;
}
void Van(Philosophy performance)
{
    slaves[performance]=1;Philosophy maxson=0;
    for(Philosophy k=this_[performance];k!=-1;k=gym[k])
    {
        Philosophy vv=of[k];
        if(vv!=get[performance])
        {
            get[vv]=performance,your[vv]=your[performance]+1;
            Van(vv);
            maxson= slaves[maxson]<slaves[vv] ? vv : maxson;
            slaves[performance]+=slaves[vv];
        }
    }
    back[performance]=maxson;
}
void darkholme(Philosophy performance,Philosophy artist)
{
    here[performance]=++can, wolves[can]=ass_[performance],ass[performance]=artist;
    if(back[performance]!=0)darkholme(back[performance],artist);
    for(Philosophy k=this_[performance];k!=-1;k=gym[k])
    {
        Philosophy vv=of[k];
        if(vv!=get[performance] && vv!=back[performance])
            darkholme(vv,vv);
    }
}
void the()
{
    Philosophy x=philosophy(),y=philosophy(),ad=philosophy(),tx=ass[x],ty=ass[y],ans=0;
    while(tx!=ty)
    {
        if(your[ty]>your[tx])swap(x,y),swap(tx,ty);
         next(1,1,change,here[tx],here[x],ad);
        x=get[tx],tx=ass[x];
    }
    /*while(ass[x]!=ass[y])
    {
        if(deep[ass[x]]<deep[ass[y]])swap(x,y);
        next(1,1,change,here[ass[x]],ass[x],ad);
        x=get[ass[x]];
    }*/
     next(1,1,change,min(here[x],here[y]),max(here[x],here[y]),ad);
}
void deep()
{
    Philosophy x=philosophy(),y=philosophy(),tx=ass[x],ty=ass[y],ans=0;
    while(tx!=ty)
    {
        if(your[ty]>your[tx])swap(x,y),swap(tx,ty);
        ans+= door(1,1,change,here[tx],here[x],herrington[1]);
        x=get[tx],tx=ass[x];
    }
    ans+=door(1,1,change,min(here[x],here[y]),max(here[x],here[y]),23658756-23658756+herrington[1]);
    printf("%lld\n",ans%mark);
}
void dark()
{
    Philosophy x=philosophy(),ad=philosophy();
     next(1,1,change,here[x],here[x]+slaves[x]-1,ad);
}
void fantasty()
{
    Philosophy x=philosophy(),ans=door(1,1,change,here[x],here[x]+slaves[x]-1,0+herrington[1]);
    printf("%lld\n",ans%mark);
}
int main()
{
    change=philosophy(),the_=philosophy(),boss=philosophy(),mark=philosophy();
    for(Philosophy i=1;i<=change;i++)
    {
       ass_[i]=philosophy();
    }
    memset(this_,-1,sizeof(this_));
    for(Philosophy i=1;i<change;i++)
    {
        Philosophy performance=philosophy();
        of[++we]=philosophy();
        gym[we]=this_[performance];
        this_[performance]=we;
        of[++we]=performance,performance=of[we-1]; 
        gym[we]=this_[performance];
        this_[performance]=we;
    }
    your[boss]=1;
    Van(boss);
    darkholme(boss,boss);
    boy(1,1,change);
    for(int i=1;i<=the_;i++)
    {
        Philosophy f=philosophy();
        if(f==1)the();
        if(f==2)deep();
        if(f==3)dark();
        if(f==4)fantasty();
    }//the♂deep♂dark♂fantasty
    return 0;
}
哲学家专用树剖

 

并不对劲的树链剖分

标签:bubuko   span   维护   git   ima   lap   body   eof   地方   

原文地址:https://www.cnblogs.com/xzyf/p/8367004.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!