并不对劲的树链剖分

听上去像是熟练剖粪。

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

重链上的点在线段树上是一段连续是空间,所以要使每条路径上的点少一些。大致想一个贪心策略,可以尽量往点多的地方分重链。这样的话,从点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;
}

哲学家专用树剖

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

时间: 2024-10-19 18:31:49

并不对劲的树链剖分的相关文章

BZOJ 2243: [SDOI2011]染色 树链剖分

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1886  Solved: 752[Submit][Status] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. In

bzoj 2243: [SDOI2011]染色 线段树区间合并+树链剖分

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 7925  Solved: 2975[Submit][Status][Discuss] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完

bzoj3694: 最短路(树链剖分/并查集)

bzoj1576的帮我们跑好最短路版本23333(双倍经验!嘿嘿嘿 这题可以用树链剖分或并查集写.树链剖分非常显然,并查集的写法比较妙,涨了个姿势,原来并查集的路径压缩还能这么用... 首先对于不在最短路径树上的边x->y,设t为最短路径树上lca(x,y),则t到y上的路径上的点i到根的距离都可以用h[x]+dis[x][y]+h[y]-h[i](h[]为深度)来更新,因为h[i]一定,只要让h[x]+dis[x][y]+h[y]最小就行,这里用树剖直接修改整条链上的数,就可以过了. 并查集的

洛谷 P3384 【模板】树链剖分

题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和 操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z 操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和 输入输出格式 输入格式: 第一行包含4个正整数N.M.R.P,分别表示树的结点个数.操作个数

bzoj1036 树的统计(树链剖分+线段树)

1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 15120  Solved: 6141[Submit][Status][Discuss] Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I

SPOJ QTREE Query on a tree ——树链剖分 线段树

[题目分析] 垃圾vjudge又挂了. 树链剖分裸题. 垃圾spoj,交了好几次,基本没改动却过了. [代码](自带常数,是别人的2倍左右) #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define maxn 20005 int T,n,fr[maxn],h[maxn],to[maxn],ne[maxn]

树链剖分简(单)介(绍)

树链剖分可以算是一种数据结构(一大堆数组,按照这个意思,主席树就是一大堆线段树).将一棵树分割成许多条连续的树链,方便完成一下问题: 单点修改(dfs序可以完成) 求LCA(各种乱搞也可以) 树链修改(修改任意树上两点之间的唯一路径) 树链查询 (各种操作)  前两个内容可以用其他方式解决,但是下面两种操作倍增.st表,dfs序就很难解决(解决当然可以解决,只是耗时长点而已).下面开始步入正题. 树链剖分的主要目的是分割树,使它成一条链,然后交给其他数据结构(如线段树,Splay)来进行维护.常

bzoj1146整体二分+树链剖分+树状数组

其实也没啥好说的 用树状数组可以O(logn)的查询 套一层整体二分就可以做到O(nlngn) 最后用树链剖分让序列上树 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 inline int read() 7 { 8 int x=0,f=1,ch=getchar(); 9 while(ch<

BZOJ 1036: [ZJOI2008]树的统计Count [树链剖分]

1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 14302  Solved: 5779[Submit][Status][Discuss] Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I