并不对劲的树链剖分

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并不对劲的树链剖分相关的知识,希望对你有一定的参考价值。

并不对劲的片手流在为很对劲的太刀流调树剖时发现线段树写错了的时候整个人都不好了,决定反驳隔壁很对劲的太刀流并与之针锋相对。

很对劲的太刀流在这里->

听上去像是熟练剖粪。

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

重链上的点在线段树上是一段连续是空间,所以要使每条路径上的点少一些。大致想一个贪心策略,可以尽量往点多的地方分重链。这样的话,从点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 
#define re register
#define m ((l+r)>>1)
#define ls (node<<1)
#define rs (node<<1|1)
#define vv v[k]
using namespace std;
LL n,q,r,p;
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;
inline 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;
}
inline void write(LL x)
{
    int ff=0;char ch[15];
    if(x<0)
    {
        x=-x;
        putchar(\'-\');
    }
    while(x)ch[++ff]=(x%10)+\'0\',x/=10;
    if(ff==0)putchar(\'0\');
    while(ff)putchar(ch[ff--]);
    putchar(\'\\n\');
}struct SegmentTree
{
    LL tre[maxn<<2],mark[maxn<<2];
    LL a[maxn],yes[maxn<<2];
    void pushup(LL node)
    {
        tre[node]=(tre[ls]+tre[rs])%p;
    }
    void pushdown(LL node,LL l,LL r)
    {
        if(l<r && mark[node])
        {
            tre[ls]+=mark[node]*(m-l+1);
            tre[rs]+=mark[node]*(r-m);
            mark[ls]+=mark[node];
            mark[rs]+=mark[node];
            mark[node]=0;
            tre[ls]%=p;tre[rs]%=p;mark[ls]%=p;mark[rs]%=p;
        }
    }
    void build(LL node,LL l,LL r)
    {
        if(l==r)
        {
            tre[node]=a[l];
            return;
        }
        build(ls,l,m);
        build(rs,m+1,r);
        pushup(node);
    }
    LL ask(LL node,LL l,LL r,LL x,LL y)
    {
        pushdown(node,l,r);
        if(x<=l && r<=y)
            return tre[node]%p;
        if(y<l || r<x)
            return 0;
        LL lans=ask(ls,l,m,x,y),rans=ask(rs,m+1,r,x,y);
        return (lans+rans)%p;
    }
    void add(LL node,LL l,LL r,LL x,LL y,LL k)
    {
        pushdown(node,l,r);
        if(x<=l && r<=y)
        {
            mark[node]+=k;
            tre[node]+=k*(r-l+1);
            tre[node]%=p,mark[node]%=p;
            return;
        }
        if(y<l || r<x)
            return;
        add(ls,l,m,x,y,k);
        add(rs,m+1,r,x,y,k);
        pushup(node);
    }
}t;
inline void getson(LL u)
{
    siz[u]=1;LL maxson=0;
    for(re LL k=fir[u];k!=-1;k=nxt[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;
}
inline void gettop(LL u,LL anc)
{
    mp[u]=++tim, t.a[tim]=s[u],top[u]=anc;
    if(son[u]!=0)gettop(son[u],anc);
    for(re LL k=fir[u];k!=-1;k=nxt[k])
    {
        if(vv!=fa[u] && vv!=son[u])
            gettop(vv,vv);
    }
}
inline 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+= t.ask(1,1,n,mp[tx],mp[x]);
        x=fa[tx],tx=top[x];
    }
    ans+=t.ask(1,1,n,min(mp[x],mp[y]),max(mp[x],mp[y]));
    write(ans%p);
}
inline 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);
        t.add(1,1,n,mp[tx],mp[x],ad);
        x=fa[tx],tx=top[x];
    }
    t.add(1,1,n,min(mp[x],mp[y]),max(mp[x],mp[y]),ad);
}
inline void get_kid_num()
{
    LL x=read(),ans=t.ask(1,1,n,mp[x],mp[x]+siz[x]-1);
    write(ans%p);
}
inline void add_kid_num()
{
    LL x=read(),ad=read();
    t.add(1,1,n,mp[x],mp[x]+siz[x]-1,ad);
}
int main()
{
    n=read(),q=read(),r=read(),p=read();
    for(re LL i=1;i<=n;i++)
    {
       s[i]=read();
    }
    memset(fir,-1,sizeof(fir));
    for(re 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);
    t.build(1,1,n);
    for(re int i=1;i<=q;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 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;
}
哲学家专用树剖
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define ls (n<<1)
#define rs (n<<1|1)
#define mi ((l+r)>>1)
#define vv v[k]
#define pushup(n) tr[n]=(tr[ls]+tr[rs])%p
#define maxn 100010 
#define add(a,b) a=((a)+(b))%p
using namespace std;
struct SegmentTree
{
    int tr[maxn*4],mk[maxn*4],a[maxn],p;
    void pushdown(int n,int l,int r){
        if(l<r && mk[n]!=0){
            add(tr[ls],mk[n]*(mi-l+1));
            add(tr[rs],mk[n]*(r-mi));
            add(mk[ls],mk[n]);add(mk[rs],mk[n]);
            mk[n]=0; 
        }
    }
    void build(int n,int l,int r){
        if(l==r){tr[n]=a[l];return;}
        build(ls,l,mi);
        build(rs,mi+1,r);
        pushup(n); 
    }
    int addask(int n,int l,int r,int x,int y,int k){
        pushdown(n,l,r);
        if(x<=l && r<=y){
            add(tr[n],k*(r-l+1));
            add(mk[n],k);
            return tr[n];
        } 
        if(y<l || r<x)return 0;
        int lans=addask(ls,l,mi,x,y,k);
        int rans=addask(rs,mi+1,r,x,y,k);
        pushup(n);
        return (lans+rans)%p;
    }
}t;
int n,p,rt,q,w[maxn],fir[maxn],nxt[maxn*2],v[maxn*2],cnt;
int fa[maxn],son[maxn],siz[maxn],mp[maxn],top[maxn],dep[maxn],tim;
void addedge(int u1,int v1){
    v[cnt]=v1,nxt[cnt]=fir[u1],fir[u1]=cnt++;
    v[cnt]=u1,nxt[cnt]=fir[v1],fir[v1]=cnt++;
}
void getson(int u){
    siz[u]=1;
    for(int k=fir[u];k!=-1;k=nxt[k]){
        if(vv==fa[u])continue;
        fa[vv]=u;
        dep[vv]=dep[u]+1;
        getson(vv);
        siz[u]+=siz[vv];
        if(son[u]==0 || siz[vv]>siz[son[u]])son[u]=vv; 
    }
}
void gettop(int u,int anc){
    mp[u]=++tim,t.a[tim]=w[u],top[u]=anc;
    if(son[u])gettop(son[u],anc);
    for(int k=fir[u];k!=-1;k=nxt[k]){
        if(vv==fa[u] || vv==son[u])continue;
        gettop(vv,vv);    
    }
}
void addaskroad(int f){
    int x,y,ad=0,ans=0;scanf("%d%d",&x,&y);
    int tx=top[x],ty=top[y];if(f)scanf("%d",&ad);
    while(tx!=ty){
        if(dep[tx]<dep[ty])swap(tx,ty),swap(x,y);
        add(ans,t.addask(1,1,n,min(mp[x],mp[tx]),max(mp[x],mp[tx]),ad));
        x=fa[tx];tx=top[x];
    }
    add(ans,t.addask(1,1,n,min(mp[x],mp[y]),max(mp[x],mp[y]),ad));
    if(!f)printf("%d\\n",ans);
}
void addaskson(int f){
    int x,ad=0,ans=0;scanf("%d",&x);if(f)scanf("%d",&ad);
    add(ans,t.addask(1,1,n,mp[x],mp[x]+siz[x]-1,ad));
    if(!f)printf("%d\\n",ans);
}
int main(){
    scanf("%d%d%d%d",&n,&q,&rt,&p);t.p=p;
    memset(fir,-1,sizeof(fir));
    for(int i=1;i<=n;i++)scanf("%d",&w[i]); 
    int x,y;
    for(int i=1;i<=n-1;i++){scanf("%d%d",&x,&y);addedge(x,y);}
    getson(rt);gettop(rt,rt);t.build(1,1,n);
    while(q--){
        int fff;scanf("%d",&fff);
        if(fff<=2)addaskroad((fff-1)^1);
        [2016北京集训测试赛]奇怪的树-[树链剖分]

HDU 5044 Tree(树链剖分)

BZOJ 2243 染色 | 树链剖分模板题进阶版

LibreOJ #139 树链剖分 [树链剖分,线段树]

UVA - 12424 Answering Queries on a Tree(十棵线段树的树链剖分)

POJ 2763 /// 基于边权的树链剖分