1. 程式人生 > >長鏈剖分(學習筆記)

長鏈剖分(學習筆記)

很久很久以前 e r k k i e r k k o

erkkierkko 給我們分享了一下這個東西,好像和 d s u   o n  
t r e e dsu\ on\ tree
的思想很像,都是長鏈直接轉移,短鏈暴力算

其中有一個很有意思的東西就是指標動態分配陣列空間,因為一個兒子的資訊剛好是父親往左或者往右移一步,比如說深度,如果一個 d

p dp 陣列是以深度為下標的話,就可以讓指標左移右移那樣來分配空間,而短鏈長度加起來不會超過 n n ,所以說短鏈可以直接開新空間。

直接上例題吧
CF1009F Dominant Indices
記錄 f [ u ] [ i ] f[u][i] 表示 u u 為根的子樹中到根距離為 i i 的點的個數
f [ u ] [ i ] = v f [ v ] [ i 1 ] f[u][i]=\sum_{v}f[v][i-1] ,如此就可以用上述做法來解決空間問題

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1000005
using namespace std;

inline int rd(){
    int x=0,f=1;char c=' ';
    while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
    while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
    return x*f;
}
inline int min(int x,int y){return x<y?x:y;}

int n,cnt,head[N],to[N<<1],nxt[N<<1];
int len[N],tmp[N],son[N],*f[N],*id=tmp,ans[N];

inline void add(int x,int y){
    to[++cnt]=y; nxt[cnt]=head[x]; head[x]=cnt;
}

void dfs(int u,int fa){
    for(int i=head[u];i;i=nxt[i])
        if(to[i]!=fa){
            dfs(to[i],u);
            if(len[to[i]]>len[son[u]]) son[u]=to[i];
        }
    len[u]=len[son[u]]+1;
}

void DP(int u,int fa){
    f[u][0]=1;
    if(son[u]) f[son[u]]=f[u]+1,DP(son[u],u),ans[u]=ans[son[u]]+1;
    for(int i=head[u];i;i=nxt[i]){
        int v=to[i]; if(v==fa||v==son[u])continue;
        f[v]=id; id+=len[v]; DP(v,u);
        for(int j=1;j<=len[v];j++){
            f[u][j]+=f[v][j-1];
            if(f[u][j]>f[u][ans[u]]) ans[u]=j;
            else if(f[u][j]==f[u][ans[u]]) ans[u]=min(ans[u],j);
        }
    }
    if(f[u][ans[u]]==1) ans[u]=0;
}

int main(){
    n=rd();
    for(int i=1;i<n;i++){
        int x=rd(),y=rd();
        add(x,y); add(y,x);
    }
    dfs(1,0); f[1]=id; id+=len[1];
    DP(1,0);
    for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
    return 0;
}

bzoj4543: [POI2014]Hotel加強版
同樣的方法
f [ u ] [ i ] f[u][i] 表示 u u 為根的子樹,到 u u 距離為 i i 的點的個數, g [ u ] [ i ] g[u][i] 表示到中心的距離為 d d u u 到中心距離為 d i d-i

f [ u ] [ 0 ] = 1 f[u][0]=1
a n s + = g [ u ] [ 0 ] ans+=g[u][0]

枚舉出邊,一邊列舉一邊更新保證不重複計算,每次枚舉出邊的時候再列舉 i i

f [ u ] [ i ] + = f [ v ] [ i 1 ] f[u][i]+=f[v][i-1]

g [ u ] [ i 1 ] + = g [ v ] [ i ] g[u][i-1]+=g[v][i]

g [ u ] [ i + 1 ] + = f [ u ] [ i + 1 ] × f [ v ] [ i ] g[u][i+1]+=f[u][i+1]\times f[v][i]

a n s + = f [ u ] [ i 1 ] × g [ v ] [ i ] + g [ u ] [ i + 1 ] × f [ v ] [ i ] ans+=f[u][i-1]\times g[v][i]+g[u][i+1]\times f[v][i]

然後再用指標動態分配空間的方法就好了, f , g f,g 的轉移剛好是反著的

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 100005
#define LL long long
using namespace std;
 
inline int rd(){
    int x=0,f=1;char c=' ';
    while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
    while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
    return x*f;
}
 
int n,cnt,head[N],to[N<<1],nxt[N<<1];
int len[N],son[N];
LL ans,*f[N],*g[N],tmp[N<<2],*id=tmp;
 
inline void add(int x,int y){
    to[++cnt]=y; nxt[cnt]=head[x]; head[x]=cnt;
}
 
void dfs(int u,int fa){
    for(int i=head[u];i;i=nxt[i]){
        int v=to[i];
        if(v!=fa){
            dfs(v,u);
            if(len[v]>len[son[u]]) son[u]=v;
        }
    }
    len[u]=len[son[u]]+1;
}
 
void DP(int u,int fa){
    if(son[u]) f[son[u]]=f[u]+1,g[son[u]]=g[u]-1,DP(son[u],u);
    f[u][0]=1; ans+=g[u][0];
    for(int i=head[u];i;i=nxt[i]){
        int v=to[i]; if(v==fa||v==son[u]) continue;
        f[v]=id; id+=len[v]<<1; g[v]=id; id+=len[v]<<1; DP(v,u);
        for(int j=0;j<len[v];j++
            
           

相關推薦

學習筆記

很久很久以前 e r k k

對LCA、樹上倍增、樹&和LCTLink-Cut Tree學習

LCA what is LCA & what can LCA do ,即最近公共祖先 在一棵樹上,兩個節點的深度最淺的公共祖先就是LCALCALCA (自己可以是自己的祖先) 很簡單,我們可以快速地進入正題了 下圖中的樹,444和555的LCALCA

【BZOJ3252】攻略,貪心

貪心 etc mes span cstring || 題解 prior esp 【BZOJ3252】攻略(長鏈剖分,貪心) 題面 BZOJ 給定一棵樹,每個點有點權,選定\(k\)個葉子,滿足根到\(k\)個葉子的所有路徑所覆蓋的點權和最大。 題解 一個假裝是對的貪心: 每

【CF1009F】 Dominant Indices

ORC += pre %d str http [1] .com class 題目鏈接 \(O(n^2)\)的\(DP\)很容易想,\(f[u][i]\)表示在\(u\)的子樹中距離\(u\)為\(i\)的點的個數,則\(f[u][i]=\sum f[v][i-1]\) 長鏈

「vijos」lxhgww的奇思妙想

題目 lxhgww在樹上玩耍時,LZX2019走了過來。lxhgww突然問道:“我現在的k級祖先是誰?”LZX2019答道:“不是我嗎?”。接著lxhgww就用教主之力讓LZX2019消失了,現在他轉過頭準備向你求助。   長鏈剖分的板子(又是亂搞優化暴力) 對於每一個點,我們定義它深度最深

BZOJ4543/BZOJ3522 [POI2014]Hotel加強版

題目好神仙……這個叫長鏈剖分的玩意兒更神仙…… 考慮dp,設\(f[i][j]\)表示以\(i\)為根的子樹中到\(i\)的距離為\(j\)的點的個數,\(g[i][j]\)表示\(i\)的子樹中有\(g[i][j]\)對點深度相同,他們到LCA的距離為\(d\),且他們的LCA到\(i\)的距離為\(d-

學習筆記

data str 繼承 write class 一個 stdin init true 長鏈剖分 長鏈剖分用於優化一些特殊的dp,可以將某些\(O(n)\)的時間復雜度降為均攤\(O(1)\)。 感覺這玩意兒大部分東西都和樹鏈剖分挺像,理解的時候可以照著輕重鏈剖分那種去理解

BZOJ 3653: 談笑風生離線, , 字尾和

題意 給你一顆有 \(n\) 個點並且以 \(1\) 為根的樹。共有 \(q\) 次詢問,每次詢問兩個引數 \(p, k\) 。詢問有多少對點 \((p, a, b)\) 滿足 \(p,a,b\) 為三個不同的點,\(p, a\) 都為 \(b\) 的祖先,且 \(p\) 到 \(a\) 的距離不能超過 \(

2019.01.06 vijos lxhgww的奇思妙想

傳送門 長鏈剖分模板題。 題意簡述:允許 O ( n

three

給出一棵 n 個點的無根樹,請在這棵樹上選三個互不相同的節點,使得這個三個節點兩兩之間距離相等,輸出方案數即可。n<=100000 這個題做法很多,但是 O

雅禮2017 WC模擬(1.21) :看門人

題意: 給一棵樹,邊長度為1且帶有權值,每個點有[li,ri][li,ri] ,求其子樹中經過他的長度在[li,ri][li,ri]的路徑的權值的最小值。 (n≤1e6)(n≤1e6) 題解: 要求O(nlogn)O(nlog⁡n),可以使用長鏈剖分解決

[WC2010]重建計劃

www solution www. eight segment std 假設 距離 include 傳送門 Description Solution 時隔多年,補上了這題的長鏈剖分寫法 感覺比點分治要好寫的多 我們假設\(pos\)是當前點的\(dfn\)

洛谷P3384

最大 com 比較 gpo 題目 -- utc tdi r+ 我們有時候遇到這樣一類題目,讓我們維護樹上路徑的某些信息,這個時候發現我們無法用線段樹或者樹狀數組來維護這些信息,那麽我們就有著一種新的數據結構,樹剖:將一棵樹劃分成若幹條鏈,用數據結構去維護每條鏈,復雜度為O(

bzoj1036: [ZJOI2008]樹的統計Count+線段樹維護

pro uil AR 總數 include TP ID tdi using bzoj1036: [ZJOI2008]樹的統計Count   Time Limit: 10 Sec   Memory Limit: 162 MB Description   一棵樹上有n個節點

HDU - 6393 Traffic Network in Numazu+基環樹

就是 fine pac ace str and mes scanf != http://acm.hdu.edu.cn/showproblem.php?pid=6393 題意 給n個點和n條邊的圖,有兩種操作,一種修改邊權,另一種查詢u到v的最短路。 分析 n個點和n

遙遠的國度,換根

100% lca 成了 修改 由於 nod ccf -- date 遙遠的國度 題目描述 zcwwzdjn在追殺十分sb的zhx,而zhx逃入了一個遙遠的國度。當zcwwzdjn準備進入遙遠的國度繼續追殺時,守護神RapiD阻攔了zcwwzdjn的去路,他需要zcwwzdj

[USACO15DEC]最大流Max Flow,線段樹

FJ給他的牛棚的N(2≤N≤50,000)個隔間之間安裝了N-1根管道,隔間編號從1到N。所有隔間都被管道連通了。 FJ有K(1≤K≤100,000)條運輸牛奶的路線,第i條路線從隔間si運輸到隔間ti。一條運輸路線會給它的兩個端點處的隔間以及中間途徑的所有隔間帶來一個單位的運輸壓力,你需要計算壓力最大的隔

洛谷P3384 - 樹模板題

題目連結 https://www.luogu.org/problemnew/show/P3384 【描述】 樹鏈剖分模板題,記一下板子 #include<bits/stdc++.h> #define node tree[id] #define lson tree[i

2的較高階應用P3384 【模板】樹

參照洛谷模板 P3384 【模板】樹鏈剖分 題意: 給你一棵包含n個結點的樹,現要求你支援以下操作: 1.x到y結點最短路徑上所有節點的值都加上z; 2.求樹從x到y結點最短路徑上所有節點的值之和; 3.將以x為根節點的子樹內所有節點值都加上z; 4.求以x為根節點

模板題

【描述】 樹鏈剖分模板題,記一下板子 #include<bits/stdc++.h> #define node tree[id] #define lson tree[id<<1] #define rson tree[id<<