dsu on tree (樹上啓發式合併) 詳解

一直都沒出過算法詳解,昨天心血來潮想寫一篇,因而 dsu on tree 它來了html

一、前置技能

1.鏈式前向星(vector 建圖)算法

2.dfs 建樹markdown

3.剖分輕重鏈,輕重兒子3d

重兒子 一個結點的全部兒子中擁有最多子樹的兒子
輕兒子 一個結點的全部兒子中不是重兒子的兒子
重邊 父親與重兒子的連邊
輕邊 父親與輕兒子的連邊
重鏈 一堆重邊鏈接而成的鏈
輕鏈 一堆輕邊鏈接而成的鏈

二、什麼是 dsu on tree(樹上啓發式合併) ?

dsu on tree 其實就是個優雅的暴力算法,和它一塊兒共被稱爲優雅暴力的算法還有莫隊
所謂優雅的暴力大概是指:「優雅思想,暴力的操做」
例如莫隊咱們知道它是將整個區間分塊,再將詢問的區間排序,最後暴力的維護全部詢問的區間
其中 "整個區間分塊,詢問的區間排序" 爲優雅的思想,而 "暴力的維護全部詢問的區間" 爲暴力的操做
由於須要將詢問的區間排序,咱們就須要先將詢問的區間保存下來,也就是要離線
dsu on tree 和莫隊相似,也須要離線(它們同屬於靜態算法)code

dsu on tree 優雅的思想:

對於以 u 爲根的子樹htm

①. 先統計它輕子樹(輕兒子爲根的子樹)的答案,統計完後刪除信息blog

②. 再統計它重子樹(重兒子爲根的子樹)的答案 ,統計完後保留信息排序

③. 而後再將重子樹的信息合併到 u上get

④. 再去遍歷 u 的輕子樹,而後把輕子樹的信息合併到 u 上table

⑤. 判斷 u 的信息是否須要傳遞給它的父節點(u 是不是它父節點的重兒子)

dsu on tree 暴力的操做

dsu on tree 暴力的操做體現於統計答案上(不一樣的題目統計方式不同)

三、dsu on tree 的過程演示及代碼

1.圖示

  • 1 的重兒子爲 2,輕兒子爲 3

  • 2 的重兒子爲 4,輕兒子爲 5

  • 3 沒有重兒子,沒有輕兒子

  • 4 的重兒子爲 6,沒有輕兒子

  • 5 的重兒子爲 7,沒有輕兒子

  • 6 沒有重兒子,沒有輕兒子

  • 7 沒有重兒子,沒有輕兒子

爲了更好觀看,咱們將節點與其重兒子的連線描紅

咱們從根節點1進入,先找1的輕兒子,發現3,進入3

3沒有別的兒子能夠進入了,因而統計3的信息

統計完後即將返回父節點 1

由於1-3的邊沒有被描紅邊、3不是1的重兒子(不傳遞3的信息),因此刪除3的信息再返回 1

發現1沒有別的輕兒子了,就找重兒子,發現2,進入2

進入2後,再找2的輕兒子,發現5,進入5

發現5沒有輕兒子了,就找重兒子,發現7,進入 7

7 沒有別的兒子能夠進入了,因而統計 7 的信息

統計完後即將返回父節點 5

由於邊5-7 有被描紅邊、7是5的重兒子,因此保留7的信息直接返回 5(傳遞7的信息的給5)

5 全部兒子都進入過了,因而統計 5 的信息

統計完後即將範圍父節點 2

由於邊2-5 沒有被描紅邊、5不是2的重兒子,因此刪除5的信息再返回 2

發現2沒有其它輕兒子了,就找重兒子,發現4,進入4

發現4沒有其它輕兒子了,就找重兒子,發現6,進入6

6 沒有別的兒子能夠進入了,因而統計 6 的信息

統計完後即將返回父節點 4

由於邊4-6 有被描紅邊,6是4的重兒子,因此保留6的信息直接返回 4(傳遞6的信息的給4)

4 全部兒子都進入過了,因而統計 4 的信息

統計完後即將返回父節點 2

由於邊2-4 有被描紅邊,4是2的重兒子,因此保留4的信息直接返回2(傳遞4的信息的給2)

2 全部兒子都進入過了,因而統計 2 的信息

2 接受了4傳遞的信息,可是並無接受5傳遞給它的信息(被刪除了)

因而 2 再進入5(輕兒子),統計一遍以 5 爲根的子樹的信息,再將該信息合併到 2上

統計完後 2 後即將返回父節點 1

由於邊1-2 有被描紅邊,2是1的重兒子,因此保留2的信息直接返回1(傳遞2的信息的給1)

1 全部兒子都進入過了,因而統計 1 的信息

1 接受了2傳遞的信息,可是並無接受3傳遞給它的信息(被刪除了)

因而 1 再進入3(輕兒子),統計一遍以 3 爲根的子樹的信息,再將該信息合併到 1 上

至此,整個 dsu on tree 的過程結束

2.代碼

struct Edge{
	int nex , to;
}edge[N << 1];
int head[N] , TOT;
void add_edge(int u , int v) // 鏈式前向星建圖
{
	edge[++ TOT].nex = head[u] ;
	edge[TOT].to = v;
	head[u] = TOT;
}
int sz[N];   // sz[u] 表示以 u 爲根的子樹大小 
int hson[N]; // hson[u] 表示 u 的重兒子 
int HH;      // HH 表示當前根節點的重兒子 
void dfs(int u , int far)
{
	sz[u] = 1;
	for(int i = head[u] ; i ; i = edge[i].nex) // 鏈式前向星 
	{
		int v = edge[i].to;
		if(v == far) continue ;
		dfs(v , u); 
		sz[u] += sz[v];  
		if(sz[v] > sz[hson[u]]) hson[u] = v; // 選擇 u 的重兒子 
	}
}
void calc(int u , int far , int val) // 統計答案 
{
	if(val == 1) ...; // val = 1,則添加信息 
	else ...;         // val = -1,則刪除信息 
	......	
    for(int i = head[u] ; i ; i = edge[i].nex)
    {
        int v = edge[i].to;
        if(v == far || v == HH) continue ; // 若是 v 是當前根節點的重兒子,則跳過
        calc(v , u , val);
    }
} 
void dsu(int u , int far , int op)  // op 等於0表示不保留信息,等於1表示保留信息 
{
	for(int i = head[u] ; i ; i = edge[i].nex)
	{
		int v = edge[i].to;
		if(v == far || v == hson[u]) continue ; // 若是 v 是重兒子或者父親節點就跳過 
		dsu(v , u , 0);     // 先遍歷輕兒子 ,op = 0 :輕兒子的答案不作保留 
	}
	if(hson[u]) dsu(hson[u] , u , 1) , HH = hson[u];
	// 輕兒子都遍歷完了,若是存在重兒子,遍歷重兒子(事實上除了葉子節點每一個點都必然有重兒子)
	// op = 1 , 保留重兒子的信息 
	// 當前是以 u 爲根節點的子樹,因此根節點的重兒子 HH = hson[u]
	calc(u , far , 1); // 再次遍歷輕兒子統計答案
        HH = 0;			   // 遍歷結束 ,即將返回父節點,因此取消標記 HH 
	if(!op) calc(u , far , -1); // 若是 op = -1,則 u 對於它的父親來講是輕兒子,不須要將信息傳遞給它的父親 
}

4.經典例題講解

題目編號 題目連接 題解連接
CF600E https://codeforces.com/problemset/problem/600/E http://www.javashuo.com/article/p-zfgilixw-nx.html
CF570D https://codeforces.com/problemset/problem/570/D http://www.javashuo.com/article/p-amgiyhws-nx.html
CF208E https://codeforces.com/problemset/problem/208/E http://www.javashuo.com/article/p-eyebfsoc-nx.html
CF246E https://codeforces.com/problemset/problem/246/E http://www.javashuo.com/article/p-wgeyxxxw-nx.html
CF1009F https://codeforces.com/problemset/problem/1009/F http://www.javashuo.com/article/p-zcmargri-nx.html
CF375D https://codeforces.com/problemset/problem/375/D http://www.javashuo.com/article/p-dphgfsxw-nx.html
wannafly Day2 E https://ac.nowcoder.com/acm/contest/4010/E?&headNav=acm http://www.javashuo.com/article/p-ukyhjnjg-nx.html
ccpc2020長春站F題 https://codeforces.com/gym/102832/problem/F http://www.javashuo.com/article/p-xsfsjfat-nx.html
洛谷P4149 https://www.luogu.com.cn/problem/P4149 http://www.javashuo.com/article/p-rsramweu-nx.html

5.難題進階

這是道較難的題,據說這也是 dsu on tree 的發明人專門爲這個算法出的題

題目編號 題目連接 題解連接
CF741D https://codeforces.com/contest/741/problem/D http://www.javashuo.com/article/p-pthqnupk-nx.html
┏┛ ┻━━━━━┛ ┻┓  
    ┃      ┃  
    ┃   ━         ┃  
    ┃ ┳┛   ┗┳     ┃  
    ┃             ┃  
    ┃   ┻       ┃  
    ┃            ┃  
    ┗━┓   ┏━━━┛  
      ┃   ┃   神獸保佑  
      ┃   ┃   代碼無BUG!  
      ┃   ┗━━━━━━━━━┓  
      ┃           ┣┓  
      ┃             ┏┛  
      ┗━┓ ┓ ┏━━━┳ ┓ ┏━┛  
          ┃ ┫ ┫   ┃ ┫ ┫  
          ┗━┻━┛   ┗━┻━┛
相關文章
相關標籤/搜索