1. 程式人生 > >ZCMU2016: 不存在的樹 (2017浙江中醫藥校賽) (樹鏈剖分)

ZCMU2016: 不存在的樹 (2017浙江中醫藥校賽) (樹鏈剖分)

解析:

樹鏈剖分的模板題,再套上一個線段樹模板就可以了。

這裡需要注意的是每找到一條鏈的過程中,這條鏈的點的線上段樹中的編號是連續的。

就是第一條重鏈有n個點,編號就是1,...n

第二條重鏈有n-k個點,編號就是n+1...2n-k

以此類推,這樣按照這些編號插入到線段樹就可以了。

這些處理做完之後,就可以一一查詢了

還有就是這道題我用vector存邊就TLE了,用陣列存就過了,不知道是我哪寫錯了還是這兩種方式的問題

樹鏈剖分大神講解:點選開啟連結

以下內容屬於摘錄:

其實樹鏈剖分就是把邊雜湊到線段樹上的資料結構。

性質:從根到某一點的路徑上輕邊、重邊的個數都不大於logn。

所以這樣查詢的時間複雜度相當於log2(n)

樹鏈剖分可以解決很多問題,輔助一些線段樹之類的資料結構可以解決一些樹上修改的問題。還可以求LCA,不過複雜度比RMQ實現的LCA多一個log。

證明如下:

(1)

由於任一輕兒子對應的子樹大小要小於父節點所對應子樹大小的一半

因此從一個輕兒子沿輕邊向上走到父節點後 所對應的子樹大小至少變為兩倍以上

經過的輕邊條數自然是不超過log2Nlog2N

然後由於重鏈都是間斷的 ((連續的可以合成一條))

所以經過的重鏈的條數是不超過輕邊條數+1+1

因此經過重鏈的條數也是loglog級別的

綜合可知原命題得證(這個證明zz表示看不懂)

每當學習了一個新演算法,我們最關切的就是它的時間複雜度了。
如果樹鏈剖分的時間複雜度高到爆,我們寫的時候估計得虛死。
但是可以證明,樹鏈剖分後的樹,從根節點到任意一個葉節點的路徑只會與O

(logn)O(logn)條樹鏈相交。這意味著將兩個節點逼近到同一條鏈上時,只需經過O(logn)O(logn)次跳轉。
即基於樹剖的其它操作的時間複雜度為Ω(logn)Ω(logn)(注意是下界,具體的上界取決於操作本身的附加的複雜度)。

證明思路因該是這樣的:
可以考慮一棵樹中輕邊的數量。由於從某一個節點開始,每走一條輕邊,子樹的大小都會減小一倍。因此任意一條樹鏈上只有O(logn)O(logn)條輕邊,即意味著只有O(logn)O(logn)

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;

const int MAXN = 50000 + 100;
#define INF 1147483647
#define M(a) memset(a,0,sizeof(a))
int val[MAXN],fa[MAXN],dep[MAXN],siz[MAXN],id[MAXN],top[MAXN],ran[MAXN],son[MAXN];

typedef struct ee
{
    int u,v;
    int next;
}ee;

ee edge[MAXN*2];
int head[MAXN],cnt;
int num,n;

typedef struct node
{
    int ii;
    int maxx;
    int sum;
}node;

node Btree[MAXN*4];

void addedge(int u,int v)
{
    edge[cnt].u=u;
    edge[cnt].v=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}

void dfs1(int x,int f,int d)
{
    dep[x]=d;
    siz[x]=1;
    son[x]=0;
    fa[x]=f;
    for(int i=head[x];i!=-1;i=edge[i].next)
    {
        int tmp=edge[i].v;
        if(tmp==f) continue;
        dfs1(tmp,x,d+1);
        siz[x]+=siz[tmp];
        if(siz[son[x]]<siz[tmp])
        {
            son[x]=tmp;
        }
    }
}

void dfs2(int x,int tp)
{
    top[x]=tp;
    id[x]=++num;
    ran[num]=x;
    if(son[x]) dfs2(son[x],tp);
    for(int i=head[x];i!=-1;i=edge[i].next)
    {
        int tmp=edge[i].v;
        if(tmp==fa[x]||tmp==son[x]) continue;
        dfs2(tmp,tmp);
    }

}

void build(int stu[],int l,int r,int root)
{
    if(l>r) return;
    if(l==r)
    {
        Btree[root].ii=l;
        Btree[root].maxx=stu[ran[l]];
        Btree[root].sum=stu[ran[l]];
        return;
    }

    int mid=(l+r)/2;
    build(stu,l,mid,root*2);
    build(stu,mid+1,r,root*2+1);

    Btree[root].maxx=max(Btree[root*2].maxx,Btree[root*2+1].maxx);
    Btree[root].sum=Btree[root*2].sum+Btree[root*2+1].sum;
}

void update(int root,int l,int r,int index,int val)
{
    if(l>r) return;
    if(l==r)
    {
        if(l==index)
        {
            Btree[root].maxx=Btree[root].sum=val;
        }
        return;

    }

    int mid=(l+r)/2;
    if(index<=mid)
        update(root*2,l,mid,index,val);
    else
        update(root*2+1,mid+1,r,index,val);

    Btree[root].maxx=max(Btree[2*root].maxx,Btree[2*root+1].maxx);
    Btree[root].sum=Btree[2*root].sum+Btree[2*root+1].sum;
}

int querymax(int root,int l,int r,int s,int e)
{
    if(r<s||l>e)
    {
        return -INF;
    }
    if(l>r) return -INF;
    if(s<=l&&r<=e)
    {
        return Btree[root].maxx;
    }
    int mid=(l+r)/2;
    return max(querymax(2*root,l,mid,s,e),querymax(root*2+1,mid+1,r,s,e));
}

int querysum(int root,int l,int r,int s,int e)
{
    if(r<s||l>e)
    {
        return 0;
    }
    if(l>r) return 0;
    if(s<=l&&r<=e)
    {
        return Btree[root].sum;
    }
    int mid=(l+r)/2;
    return (querysum(2*root,l,mid,s,e)+querysum(root*2+1,mid+1,r,s,e));
}


void solvemax(int a,int b)
{
    if(dep[top[a]]<dep[top[b]])
    {
        swap(a,b);
    }
    int ans=-INF;
    while(top[a]!=top[b])
    {
        //int tmp=top[a];
        ans=max(ans,querymax(1,1,n,id[top[a]],id[a]));
        a=fa[top[a]];
        if(dep[top[a]]<dep[top[b]])
        {
            swap(a,b);
        }
    }
    if(id[a]>id[b]) swap(a,b);
    ans=max(ans,querymax(1,1,n,id[a],id[b]));
    printf("%d\n",ans);

}


void solvesum(int a,int b)
{
    if(dep[top[a]]<dep[top[b]])
    {
        swap(a,b);
    }
    int ans=0;
    while(top[a]!=top[b])
    {
        //int tmp=top[a];
        ans=ans+querysum(1,1,n,id[top[a]],id[a]);
        a=fa[top[a]];
        if(dep[top[a]]<dep[top[b]])
        {
            swap(a,b);
        }
    }
    if(id[a]>id[b]) swap(a,b);
    ans+=querysum(1,1,n,id[a],id[b]);
    printf("%d\n",ans);
}


int main()
{
    int q;
    while(scanf("%d%d",&n,&q)!=EOF)
    {
        num=0;
        M(Btree);
        M(son);
        cnt=0;
        memset(head,-1,sizeof(head));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&val[i]);
        }
        for(int i=1;i<n;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            addedge(u,v);
            addedge(v,u);
        }
        dfs1(1,0,1);
        dfs2(1,1);
        int mode,a,b;
        build(val,1,n,1);
        for(int i=0;i<q;i++)
        {
            scanf("%d%d%d",&mode,&a,&b);
            if(mode==0)
            {
                solvemax(a,b);
            }
            else if(mode==1)
            {
                solvesum(a,b);
            }
            else
            {
                update(1,1,n,id[a],b);
            }
        }
    }
    return 0;
}