长链剖分

长链剖分

定义:

长链剖分主要用于解决两点之间链的问题。

实现:

思路与重链剖分相同,但是选择重儿子时选择子树深度最大的儿子。

代码:

void dfs1(int x,int fa){
    d[x]=d[fa]+1;
    f[x]=fa;
    for(int i=head[x];i;i=nxt[i]){
        int y=ver[i];
        if(y==fa) continue;
        dfs1(y,x);
        if(dep[y]>dep[son[x]]) son[x]=y;
    }
    dep[x]=dep[son[x]]+1;//子树中的最大深度
}
void dfs2(int x,int topfather){
    top[x]=topfather;
    if(son[x]) dfs2(son[x],topfather);
    for(int i=head[x];i;i=nxt[i]){
        int y=ver[i];
        if(y==f[x]||y==son[x]) continue;
        dfs2(y,y);
    }
}

当然,题目不同时,\(dfs2\) 会有所不同,具体情况具体分析。

性质:

  1. 所有链的长度和是 \(O(n)\) 级别的。
  2. 任意一个点的 \(k\) 次祖先 \(y\) 所在的长链的长度大于等于 \(k\)
  3. 任意一个点向上跳重链次数不超过 \(\sqrt{n}\) 次。

作用:

优化dp:快速合并以深度为下标的子树信息

例题:P5904 [POI2014]HOT-Hotels 加强版

我们可以根据题意设置一个 \(dp\) 转移,时间复杂度为 \(O(n^2)\)

所以,设 \(f[i][j]\) 表示以 \(i\) 为根的子树中,距离当前点为 \(j\) 的点数。

\(g[i][j]\) 表示以i为根的子树中,两个点到 \(LCA\) 的距离为 \(d\) ,并且他们的 \(LCA\)\(i\) 的距离为 \(d−j\) 的点对数。

考虑转移:

ans+=g[i][0];
ans+=g[i][j]*f[son[i]][j-1];
f[i][j]+=f[son[i]][j-1];
g[i][j]+=g[son[i]][j+1];

我们需要运用指针来将数组的空间减少,因此在转移中可以这样:

int *f[N];

.....

f[i]=f[son[i]]-1;
g[i]=g[son[i]]-1;

整棵树是链,时间复杂度将为 \(O(n)\) 。我们推广到树:

进行长链剖分,直接从重儿子转移 \(O(1)\) ,从轻儿子转移 \(O(\sum len)\)

因此总复杂度就是 \(O(n)\)

其余状态转移暴力即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
const int N=4e5+5;

int n;
int d[N],dep[N],son[N];
ll *f[N],*g[N],p[N<<2],*o=p,ans;
int nxt[N],ver[N],tot,head[N];
void add(int x,int y){
    ver[++tot]=y;
    nxt[tot]=head[x];
    head[x]=tot;
}
void dfs1(int x,int fa){
    d[x]=d[fa]+1;
    for(int i=head[x];i;i=nxt[i]){
        int y=ver[i];
        if(y==fa) continue;
        dfs1(y,x);
        if(dep[y]>dep[son[x]]) son[x]=y;
    }
    dep[x]=dep[son[x]]+1;
}
/*
设f[i][j]表示x在i的子树中,且d(x,i)=j的x的个数
  g[i][j]为满足x,y在i的子树中且d(lca(x,y),x)=d(lca(x,y),y)=d(lca(x,y),i)+j
的数对(x,y)的个数
递推公式:
ans=g[i][0]=sum f[x][j-1]*g[y][j+1](x,y属于son[i])
g[i][j]=sum g[x][j+1](x属于son[i])
f[i][j]=sum f[x][j-1](x属于son[i])
*/
void dfs2(int x,int fa){
    if(son[x]){
        f[son[x]]=f[x]+1;
        g[son[x]]=g[x]-1;
        dfs2(son[x],x);
    }
    f[x][0]=1,ans+=g[x][0];
    for(int i=head[x];i;i=nxt[i]){
        int y=ver[i];
        if(y==fa||y==son[x]) continue;
        f[y]=o,o+=dep[y]<<1;
        g[y]=o,o+=dep[y]<<1;
        dfs2(y,x);
        for(int i=0;i<dep[y];i++){
            if(i) ans+=f[x][i-1]*g[y][i];
                  ans+=g[x][i+1]*f[y][i];
        }
        for(int i=0;i<dep[y];i++){
            g[x][i+1]+=f[x][i+1]*f[y][i];
            if(i) g[x][i-1]+=g[y][i];
            f[x][i+1]+=f[y][i];
        }
    }
}
int main()
{
    cin>>n;
    for(int i=1,x,y;i<n;i++){
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    dfs1(1,0);
    f[1]=o;o+=dep[1]<<1;
    g[1]=o,o+=dep[1]<<1;
    dfs2(1,0);
    cout<<ans<<endl;
    system("pause");
    return 0;
}
posted @ 2021-07-01 10:27  Evitagen  阅读(261)  评论(0)    收藏  举报