1. 程式人生 > 實用技巧 >淺談長鏈剖分

淺談長鏈剖分

用途:優化轉移時間複雜度只和深度有關的樹形DP

思想:定義重兒子為深度最大的兒子,每次用O(1)的時間繼承來自重兒子的資訊,然後暴力合併親兒子,均攤時間複雜度O(n),均攤空間複雜度O(n)。

證明:

  時間複雜度:

    定義長鏈為最長的全是由重兒子組成的鏈,則長鏈的頂點一定是一個輕兒子。

    對於每個點,只存在於一條長鏈上,每條長鏈只會合併一次,需要O(長鏈長度)的時間,並且合併後根節點的資訊個數(根節點深度個)不會發生變化。

    所以可以大膽地把時間均勻分配到每個輕鏈上的節點,均攤O(1),

    最後子樹除去過根節點長鏈外所有節點均攤O(1),而長鏈上節點沒有合併沒有時間消耗,可看出一條鏈參與接下來的流程。

    故時間為O(n)。

  空間複雜度:

    對於每條長鏈上的節點,可以通過模擬指標的方式將資訊以O(d)的大小儲存在頂點。每個點只屬於一條鏈,故總需空間O(n)

證畢

P3565 [POI2014]HOT-Hotels

  先想DP,3個點距離相同,一定是分別到兩個點的Lca距離相同。

  所以令f[u][i]表示在u的子樹中深度為i的節點的個數,g[u][i]表示在u的子樹中還需i的距離的點對數目,轉移如下

void dfs(int fa,int u) {
    f[u][0]=1; 
    for(int e=he[u];e;e=nxt[e]) {
        int v=to[e];
        
if(v!=fa) { dfs(u,v); for(int j=1;j<=n;j++) { ans+=g[v][j]*f[u][j-1]+g[u][j]*f[v][j-1]; } for(int j=1;j<=n;j++) { g[u][j]+=g[v][j+1]+(ll)f[u][j]*f[v][j-1]; f[u][j]+=f[v][j-1]; } g[u][
0]+=g[v][1]; } } }

  用長鏈剖分進行優化(陣列可以往大里開)誰叫我不會指標呢

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int N=5005,M=2e5+5;
int n,cnt,id,to[N<<1],nxt[N<<1],he[N],dep[N],son[N],sf[N],sg[N];
ll t[M],ans;

inline void add(int u,int v) {
    to[++cnt]=v,nxt[cnt]=he[u],he[u]=cnt;
}

void dfs(int fa,int u) {
    if(son[u]) {
        sf[son[u]]=sf[u]+1;
        sg[son[u]]=sg[u]-1;
        dfs(u,son[u]);
    }
    t[sf[u]]=1,ans+=t[sg[u]];
    for(int e=he[u];e;e=nxt[e]) {
        int v=to[e];
        if(v!=fa&&v!=son[u]) {
            sf[v]=id,id+=dep[v]<<1,sg[v]=id,id+=dep[v]<<1;
            dfs(u,v);
            for (int j=0;j<dep[v];++j){
                if (j) ans+=t[sf[u]+j-1]*t[sg[v]+j];
                ans+=t[sg[u]+j+1]*t[sf[v]+j];
            }    
            for (int j=0;j<dep[v];++j){
                t[sg[u]+j+1]+=t[sf[u]+j+1]*t[sf[v]+j];
                if (j) t[sg[u]+j-1]+=t[sg[v]+j];
                t[sf[u]+j+1]+=t[sf[v]+j];
            }
        }
    }
}

void dfs1(int fa,int u) {
    for(int e=he[u];e;e=nxt[e]) {
        int v=to[e];
        if(v!=fa) {
            dfs1(u,v);
            if(dep[son[u]]<dep[v]) son[u]=v;
        }
    }
    dep[u]=dep[son[u]]+1;
}

int main() {
    scanf("%d",&n);
    for(int i=1;i<n;i++) {
        int u,v; scanf("%d%d",&u,&v);
        add(u,v),add(v,u);
    }
    dfs1(0,1);
    sf[1]=id,id+=dep[1]<<1,sg[1]=id,id+=dep[1]<<1; 
    dfs(0,1);
    printf("%lld\n",ans);
    return 0;
}