學習筆記 DSU on tree

2022-02-19 18:44:56 字數 3456 閱讀 7109

*模擬賽要結束時,ssw02問sxk:t3 你寫的什麼? *

sxk :我終於把t3調出來了

ssw02 : 666,所以你寫的什麼?

sxk :我以前還沒寫過。

*ssw02 : ???(自閉) *

考試後 sxk : 我不給你說了我寫的是 dsu on tree 嗎,我以前還沒怎麼寫過 。

ssw02 : ???

歡迎**ssw02的部落格:

當你發現一道樹上的題,有一些統計性任務,而且不帶修改,並且只能進行單點統計之類的,複雜度還只能承受 nlogn ,那麼,先別寫某些毒瘤樹套樹,dsu on tree (樹上啟發式合併) 是乙個不錯的選擇。

毒瘤noip原話 :

說實話"dsu on tree"是個極其有問題的民科叫法吧。。(沒有懟人的意思。。)

這東西幾十年前就有了啊,那個人自己yy了個鏈分治就瞎起名字。。

好的,我們切入正題,如何讓乙個 n^2 級別的暴力子樹統計變成乙個 nlogn 資料結構的演算法

我們考慮利用輕重鏈剖分的性質來對演算法複雜度進行優化。(假設你會樹剖)

假設我們正在遞迴處理子樹 u 。

我們進行如下操作(亂搞):

1.我們先暴力跑 u 的輕兒子所在的子樹,同時我們刪除遞迴的貢獻值。

2.然後我們搞 u 的重兒子,這個時候我們要把貢獻給算上了。

3.我們發現子樹的答案還沒有更新,怎麼辦?再把 u 的輕兒子都遞迴一遍,同時統計累計貢獻 。

4.這時候就可以得出子樹的答案了。

5.然後,沒有然後了吧,我們好像在遞迴時就處理了每個節點吧。

實現**:

void  deal( int u , int fa , int val )

}void dfs2( int u , int fa , int opt )

if( son[ u ] )dfs2( son[ u ] , u , 1 ) , s = son[ u ] ;

deal( u , fa , 1 ) ;//遞迴處理子樹,統計輕兒子貢獻

ans[ u ] = sum , s = 0 ;

if( !opt )deal( u , fa , -1 ) , sum = 0 , mx = 0 ;//memset上仙

}

同樹鏈剖分(這裡指輕重鏈剖分,不是長鏈剖分)。

注意,清除貢獻的時候,如果你用了 memset ,emmmm,恭喜您上天了 。

清除**:

ans[ u ] = sum , s = 0 ; 

if( !opt )deal( u , fa , -1 ) , sum = 0 , mx = 0 ;//memset上仙

不信你看:下面是用 memset 的

題目傳送門

給一棵根為1的樹,每次詢問子樹顏色種類數

ac**:

#includeusing namespace std ;

#define ll long long

const int maxn = 100005 ;

inline int read()

int n , m , col[ maxn ] , son[ maxn ] , size[ maxn ] , cnt[ maxn ] ;

int tot = 1 , s , sum , ans[ maxn ] , head[ maxn ] , nex[ maxn*2 ] , to[ maxn*2 ] ;

void add( int x , int y )

void dfs( int u , int fa )

}void deal( int u , int fa , int val )

}void dfs2( int u , int fa , int opt )

if( son[ u ] )dfs2( son[ u ] , u , 1 ) , s = son[ u ] ;

deal( u , fa , 1 ) ;//遞迴處理子樹,統計輕兒子貢獻

ans[ u ] = sum , s = 0 ;

if( !opt )deal( u , fa , -1 ) , sum = 0 ;//memset上仙

}int main()

for( int i = 1 ; i <= n ; ++i )col[ i ] = read() ;

dfs( 1 , 1 ) ;

dfs2( 1 , 1 , 0 ) ;

m = read() ;

for( int i = 1 ; i <= m ; ++i )

return 0 ;

}

一棵樹有n個結點,每個結點都是一種顏色,每個顏色有乙個編號,求樹中每個子樹的最多的顏色編號的和。

ac**:

#includeusing namespace std ;

#define ll long long

const int maxn = 100005 ;

int s=0 ; char g=getchar() ; while(g>'9'||g

while( g>='0'&&g<='9' )s=s*10+g-'0',g=getchar() ; return s ;

} int n , col[ maxn ] , son[ maxn ] , size[ maxn ] , cnt[ maxn ] ;

int tot = 1 , s , head[ maxn ] , nex[ maxn*2 ] , to[ maxn*2 ] ;

ll sum , mx , ans[ maxn ] ;

void add( int x , int y )

void dfs( int u , int fa )

}void deal( int u , int fa , int val )

}void dfs2( int u , int fa , int opt )

if( son[ u ] )dfs2( son[ u ] , u , 1 ) , s = son[ u ] ;

deal( u , fa , 1 ) ;//遞迴處理子樹,統計輕兒子貢獻

ans[ u ] = sum , s = 0 ;

if( !opt )deal( u , fa , -1 ) , sum = 0 , mx = 0 ;//memset上仙

}int main()

dfs( 1 , 1 ) ;

dfs2( 1 , 1 , 0 ) ;

for( int i = 1 ; i <= n ; ++i )printf("%lld ",ans[ i ] ) ;

return 0 ;

}

學習總結 Dsu on tree 樹上啟發式合併

rt,這只是一篇小小的總結,以便將來的回顧,並不詳細講 以前也學習過啟發式合併,大概就是像樹形dp一樣在dfs上,將兒子的資訊向父親轉移,容器是map,將兒子的資訊邊轉移邊更新答案,轉移之後便將兒子的容器清空,防止空間超限。不過對於本人而言,雖然思路較為簡便,但是因為有用到map的迭代器,所以這種寫...

啟發式合併 dsu on tree 姿勢

一場比賽有兩道,好tm巧啊 這種方法其實就是通過性質優化的暴力。遍歷時輕邊優先。首先肯定是拆位了,然後可以用trie維護一下某顆子樹內的點的串,很容易統計答案。具體要看題解了 但是暴力加 刪點會t,就要用dot了。發現計算完乙個兒子的答案之後,整顆子樹都被新增到了trie中。但如果要繼續做其他兒子所...

dsu on tree 樹上啟發式合併

詳解 dsu on tree 樹上啟發式合併 演算法總結 習題 經典例題 題意 一棵樹有n個結點,每個結點都是一種顏色,每個顏色有乙個編號,求樹中每個子樹的最多的顏色編號的和。dsu on tree簡介 在o n 2 的暴力做法中,我們用cnt記錄每種顏色出現的次數,對於每個結點,遍歷這棵子樹上的所...