1. 程式人生 > 資訊 >印度痛批亞馬遜和沃爾瑪:美國電商巨頭傲慢,甚至無視法律

印度痛批亞馬遜和沃爾瑪:美國電商巨頭傲慢,甚至無視法律

長鏈剖分

定義:

長鏈剖分主要用於解決兩點之間鏈的問題。

實現:

思路與重鏈剖分相同,但是選擇重兒子時選擇子樹深度最大的兒子。

程式碼:

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;
}