集訓 Day 3 總結 虛樹 + dfs tree + 基環樹

虛樹

虛樹,顧名思義是 只關注原樹上的某些 關鍵點,在保留原樹祖孫關系的前提下建出的一棵邊數、點數大大減少的樹

適用于優化某些在整棵樹上進行 d p dp dp d f s dfs dfs 的問題

通常是題目中出現多次詢問,每次給出樹上的一些關鍵點,同時保證 ∑ 關鍵點數 ≤ n \sum關鍵點數 \leq n 關鍵點數n ,很大可能就是建出虛樹來處理

概括來說,虛樹只進行兩步操作 剪掉無用樹枝壓縮樹上路徑

Warning

有一個常見誤區:壓縮樹上路徑 的含義

在這里插入圖片描述

如圖 ,只有紅色是關鍵點,黑色加粗為虛樹上的點

若是只壓縮路徑,那么完全可以 1 ? > 4 , 1 ? > 6 1->4,1->6 1?>41?>6 連邊 ,而不需要保留 4 , 6 4,6 4,6 的 lca 3 3 3 號點

為什么要這樣保留呢?實際上,這保證了 虛樹上的邊對應原樹上的路徑是不交的

這個性質在后面題目中有大用

思想懂了,來看具體實現

build 建樹

通常有兩種建樹方式,兩次 s o r t sort sort 和 單調棧

本人通常采用前者,碼量較短

int p[2*N] , rt , len , num ;
void build( int x )
{sort( c[x].begin() , c[x].end() , cmp ) ;num = c[x].size() ;len = 0 ;p[++len] = c[x][0] ;for(int i = 1 ; i < c[x].size() ; i ++ ) {p[++len] = c[x][i] ;p[++len] = LCA( c[x][i-1] , c[x][i] ) ; // 由虛樹定義,這樣一定能把虛樹上的點都包含 }sort( p+1 , p+len+1 , cmp ) ;len = unique( p+1 , p+len+1 ) - (p+1) ; // 再去重 for(int i = 2 ; i <= len ; i ++ ) { // 恰好連了 len-1 條邊,且不重復,不成環 int x = p[i] , y = LCA( x , p[i-1] ) ;add( y , x , dep[x]-dep[y] ) ;}rt = p[1] ;
}

基于 d f s dfs dfs 序的性質,可以保證建出的虛樹是正確的,需要注意 p p p 數組需要開 2 2 2

從代碼里我們也可以得到 虛樹上的點數上界為關鍵點的 2 倍,是線性級別

例題

A

To在這里插入圖片描述
直接在原樹上跑 n n n d f s dfs dfs 顯然會超時

所以建出虛樹后直接 d f s dfs dfs 統計即可

#include<bits/stdc++.h>
using namespace std ;typedef long long LL ;
const int N = 2e5 + 100 ; int n , a[N] ;
vector<int> E[N] , c[N] ;int dep[N] , fat[N][22] , dfn[N] , tim ;
void dfs( int x , int fa )
{dep[x] = dep[fa] + 1 , fat[x][0] = fa , dfn[x] = ++tim ; for(int i = 1 ; i <= 20 ; i ++ ) fat[x][i] = fat[fat[x][i-1]][i-1] ;for(int t : E[x] ) {if( t == fa ) continue ;dfs( t , x ) ;}
}
int LCA( int x , int y )
{if( dep[x] < dep[y] ) swap( x , y ) ;for(int i = 20 ; i >= 0 ; i -- ) {if( dep[fat[x][i]] >= dep[y] ) x = fat[x][i] ;}if( x == y ) return x ;for(int i = 20 ; i >= 0 ; i -- ) {if( fat[x][i] != fat[y][i] ) x = fat[x][i] , y = fat[y][i] ;}return fat[x][0] ;
}
bool cmp ( int x , int y )
{return dfn[x] < dfn[y] ;
}struct nn
{int lst , to , val ;
}e[N] ;
int head[N] , tot ;
inline void add( int x , int y , int v )
{e[++tot] = (nn){ head[x] , y , v } ;head[x] = tot ;
}
int p[2*N] , rt , len , num ;
void build( int x )
{sort( c[x].begin() , c[x].end() , cmp ) ;num = c[x].size() ;len = 0 ;p[++len] = c[x][0] ;for(int i = 1 ; i < c[x].size() ; i ++ ) {p[++len] = c[x][i] ;p[++len] = LCA( c[x][i-1] , c[x][i] ) ; // 由虛樹定義,這樣一定能把虛樹上的點都包含 }sort( p+1 , p+len+1 , cmp ) ;len = unique( p+1 , p+len+1 ) - (p+1) ; // 再去重 for(int i = 2 ; i <= len ; i ++ ) { // 恰好連了 len-1 條邊,且不重復,不成環 int x = p[i] , y = LCA( x , p[i-1] ) ;add( y , x , dep[x]-dep[y] ) ;}rt = p[1] ;
}
LL ans ;
int siz[N] , col ;
void calc( int x , int fa )
{if( a[x] == col ) siz[x] = 1 ;else siz[x] = 0 ;for(int i = head[x] ; i ; i = e[i].lst ) {int t = e[i].to ;if( t == fa ) continue ;calc( t , x ) ;siz[x] += siz[t] ;ans += 1LL*e[i].val*siz[t]*(num-siz[t]) ;}head[x] = 0 ;
}int main()
{scanf("%d" , &n ) ;int x , y ;for(int i = 1 ; i < n ; i ++ ) {scanf("%d%d" , &x , &y ) ;E[x].push_back( y ) ;E[y].push_back( x ) ;}dfs( 1 , 0 ) ;for(int i = 1 ; i <= n ; i ++ ) {scanf("%d" , &a[i] ) ;c[a[i]].push_back( i ) ;}for(int i = 1 ; i <= n ; i ++ ) {if( c[i].size() <= 1 ) continue ;col = i ; tot = 0 ;build( i ) ;calc( rt , 0 ) ;}printf("%lld\n" , ans ) ;return 0 ;
}

?

B

To

在這里插入圖片描述
先考慮原樹上 d p dp dp ,好轉移

放到虛樹上,由于虛樹上一條邊代表了一段路徑,我們欽定它斷開時顯然應該找原樹這一段權值最小的一條邊

預處理之

int dep[N] , fat[N][22] , Min[N][22] , dfn[N] , tim ;
void dfs( int x , int fa )
{dep[x] = dep[fa] + 1 , fat[x][0] = fa , dfn[x] = ++tim ;for(int i = 1 ; i <= 20 ; i ++ ) {fat[x][i] = fat[fat[x][i-1]][i-1] ;Min[x][i] = min( Min[x][i-1] , Min[fat[x][i-1]][i-1] ) ;// 預處理}for(int i = head[x] ; i ; i = E[i].lst ) {int t = E[i].to ;if( t == fa ) continue ;Min[t][0] = E[i].val ;dfs( t , x ) ;}
}
int LCA( int x , int y )
{if( dep[x] < dep[y] ) swap( x , y ) ;for(int i = 20 ; i >= 0 ; i -- ) {if( dep[fat[x][i]] >= dep[y] ) x = fat[x][i] ;}if( x == y ) return x ;for(int i = 20 ; i >= 0 ; i -- ) {if( fat[x][i] != fat[y][i] ) x = fat[x][i] , y = fat[y][i] ;}return fat[x][0] ;
}int b[N] , p[2*N] , K , len , rt ;
bool is[N] ;
bool cmp ( int x , int y )
{return dfn[x] < dfn[y] ;
}
int Get( int x , int y )
{int res = 1e9 ;for(int i = 20 ; i >= 0 ; i -- ) {if( dep[fat[x][i]] >= dep[y] ) {res = min( res , Min[x][i] ) ;x = fat[x][i] ;}}return res ;
}
void build()
{sort( b+1 , b+K+1 , cmp ) ;len = 0 ; p[++len] = 1 , p[++len] = b[1] ;for(int i = 2 ; i <= K ; i ++ ) {p[++len] = b[i] ;p[++len] = LCA( b[i-1] , b[i] ) ; }sort( p+1 , p+len+1 , cmp ) ;len = unique( p+1 , p+len+1 ) - (p+1) ;rt = p[1] ;for(int i = 2 ; i <= len ; i ++ ) {int x = p[i] , y = LCA( p[i-1] , p[i] ) ;add1( y , x , Get(x,y) ) ;}
}
LL f[N] ;
void calc( int x , int fa )
{if( is[x] ) f[x] = LL(1e17) ;else f[x] = 0 ;for(int i = Hd[x] ; i ; i = e[i].lst ) {int t = e[i].to , v = e[i].val ;if( t == fa ) continue ;calc( t , x ) ;f[x] += min( f[t] , 1LL*v ) ;}Hd[x] = 0 ;
}
// each queryscanf("%d" , &K ) ;for(int j = 1 ; j <= K ; j ++ ) scanf("%d" , &b[j] ) , is[b[j]] = 1 ;tot1 = 0 ;build() ;calc( rt , 0 ) ;if( f[rt] >= LL(1e17) ) {printf("-1\n") ;}else printf("%lld\n" , f[rt] ) ;for(int j = 1 ; j <= K ; j ++ ) is[b[j]] = 0 ;

?

C

To 充分利用虛樹性質
在這里插入圖片描述
這道題可以告訴我們:虛樹本身是有非常多的性質的!

考慮建出了包含關鍵點的虛樹之后,討論一下所有點到最近關鍵點的情況:

在這里插入圖片描述
對于被 “剪掉的樹枝” (藍色部分):顯然需要先走到被虛樹包含 (被壓縮的 或 本身就是虛樹上節點) 的點上,

對于被 壓縮的節點 (如 5 5 5 號點) :一定與所在虛樹邊的兩端點中的一個情況相同,可以從深度較大的往上二分得到分界點

剩下虛樹上的點,我們顯然可以直接跑多源最短路,把所有關鍵點放堆里跑一次

然后就是一些 簡單(煩人)分討啦

D

To

題如其名,十分毒瘤,碼量較大

E

To

一道不太一樣的 “虛樹” 題

發現本題實際上是要 動態維護虛樹 ?( LCT ? 不會

有一個下位替代:用 s e t set set 來維護

具體來說: s e t set set 中只存關鍵點,按 d f n dfn dfn 序排序

首先一個經典結論:樹上若干個點的 L C A LCA LCA 等價于 d f n dfn dfn 序 最小和最大的兩點的 L C A LCA LCA

這樣我們就可以實時找到這個連通塊的根了,再利用 d f s dfs dfs 序的性質能夠實現部分操作

對于本題,還有一個結論

DFS 序求出后,假設關鍵點按照 DFS 序排序后是 a 1 , a 2 , . . . , a k {a_1,a_2,...,a_k} a1?,a2?,...,ak?
那么所有關鍵點形成的極小連通子樹的邊權和的兩倍等于 d i s ( a 1 , a 2 ) + d i s ( a 2 , a 3 ) + . . . + d i s ( a k , a 1 ) dis(a_1,a_2)+dis(a_2,a_3)+...+dis(a_k,a_1) dis(a1?,a2?)+dis(a2?,a3?)+...+dis(ak?,a1?)

如果是點權,那么要取 相鄰兩點路徑上除它們 L C A LCA LCA 以外的點權和,這樣求和結果是 除整個連通塊的 L C A LCA LCA 外,所有點點權的 2 2 2

畫圖理解

那么本題維護一下插入刪除時的貢獻變化就做完了

?

最后再總結一下虛樹注意事項:

  1. 用兩次 s o r t sort sort 建虛樹時要注意去重前的點數是 2 n 2n 2n 的,數組要開夠

dfs tree

顧名思義,在 有向/無向圖 中從某個點開始,按 D F S DFS DFS 的順序遍歷,每個點只經過一次,形成的一棵樹

處理圖上問題有很大作用,如 T a r j a n Tarjan Tarjan 算法實際上就是基于 d f s t r e e dfs tree dfstree

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/44591.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/44591.shtml
英文地址,請注明出處:http://en.pswp.cn/web/44591.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

11網絡層-分組轉發算法

路由 分組轉發 1&#xff09;從數據報的首部提取目的主機的IP地址D&#xff0c;得出目的網絡地址N 2&#xff09;若N就是與此路由器直接相連的某個網絡地址&#xff0c;則進行直接交付&#xff0c;不需要經過其他路由器&#xff0c;直接將數據報交付給目的主機&#xff08;這…

人為因素:為什么網絡安全不僅僅關乎技術

關注公眾號網絡研究觀獲取更多最新內容。 我們生活在一個生活與技術日益緊密交織的世界。但在構建防火墻和安裝防病毒軟件時&#xff0c;我們常常會忘記一個關鍵因素&#xff1a;人的行為。 網絡犯罪分子正是利用了人為因素&#xff0c;利用巧妙的心理戰術繞過最強大的安全措…

【MySQL基礎篇】事務

事務簡介 事務是一組操作的集合&#xff0c;它是一個不可分割的工作單位&#xff0c;事務會把所有的操作作為一個整體一起向系統提交或或撤銷操作請求&#xff0c;即這些操作要么同時成功&#xff0c;要么同時失敗。 典型事例&#xff1a;銀行轉賬操作 假設張三向李四進行轉賬…

Python:正則表達式相關整理

最近因為一些原因頻繁使用正則表達式&#xff0c;因為以前系統整理過關于正則表達式的相關知識&#xff0c;所以這里僅記錄使用期間遇到的問題。 本文內容基于re包 1. match和search方法的區別 在Python中&#xff0c;re.search和re.match都是用于匹配字符串的正則表達式函數&a…

防火墻NAT、智能選路綜合實驗

一、實驗拓撲 二、實驗要求 1&#xff0c;辦公區設備可以通過電信鏈路和移動鏈路上網(多對多的NAT&#xff0c;并且需要保留一個公網IP不能用來轉換) 2&#xff0c;分公司設備可以通過總公司的移動鏈路和電信鏈路訪問到Dmz區的http服務器 3&#xff0c;多出口環境基于帶寬比例…

Curator分布式鎖

Curator 是一個用于 Apache ZooKeeper 的客戶端庫&#xff0c;提供了更高級的抽象和工具&#xff0c;以簡化 ZooKeeper 的使用。Curator 是由 Netflix 開發的&#xff0c;并已成為分布式應用程序中使用 ZooKeeper 的事實標準。它解決了原生 ZooKeeper API 使用復雜、易出錯的問…

node js安裝、配置(Windows版)

目錄 node js 安裝 node js 全局配置 1、全局安裝路徑 2、全局緩存路徑 3、修改環境變量 pnpm安裝、卸載 全局安裝pnpm 驗證pnpm版本 卸載pnpm 1、移除全局安裝的包 2、移除pnpm cli 腳本直接安裝 npm安裝的使用命令直接卸載 node js 安裝 cmd 查看是否存在&…

容器docker 架構命令案例

文章目錄 前言一、docker1.1 為什么有docker1.2 docker架構1.3 docker 安裝1.4 docker中央倉庫1.5 docker 基本指令1.6 docker數據卷&#xff0c;掛載例&#xff1a;nginx 數據卷掛載例&#xff1a;mysql 本地持久化 1.7 鏡像制作鏡像結構dockerfile基礎指令容器生成鏡像 1.8 d…

宿主機訪問docker容器中的mysql被拒絕

問題&#xff1a; 解決方案&#xff1a; 1.進入docker中的mysql容器 docker exec -it 容器名稱/id /bin/bash 2.登錄用戶 mysql -u root -p 3.進去mysql自帶的管理數據庫mysql use mysql; 4.查詢用戶的訪問權限 SELECT user, host FROM user WHERE userroot;5.發現該用…

繪畫平臺小程序的設計

管理員賬戶功能包括&#xff1a;系統首頁&#xff0c;個人中心&#xff0c;學生管理&#xff0c;講師管理&#xff0c;課程類型管理&#xff0c;課程信息管理&#xff0c;課程購買管理&#xff0c;作業類型管理 開發系統&#xff1a;Windows 架構模式&#xff1a;SSM JDK版本&…

AURORA仿真

AURORA 仿真驗證 定義&#xff1a;AURORA是一種高速串行通信協議&#xff0c;通常用于在數字信號處理系統和其他電子設備之間傳輸數據。它提供了一種高效的方式來傳輸大量數據&#xff0c;通常用于需要高帶寬和低延遲的應用中。AURORA協議通常由Xilinx公司的FPGA器件支持&#…

golang 項目打包部署環境變量設置

最近將 golang 項目打包部署在不同環境&#xff0c;總結一下自己的心得體會&#xff0c;供大家參考。 1、首先要明確自己目標服務器的系統類型(例如 windows 或者Linux) &#xff0c;如果是Linux 還需要注意目標服務器的CPU架構(amd或者arm) 目標服務器的CPU架構可執行命令&…

對Mapper.xml文件進行深入的學習

1. 前言 既上次在Mapper.xml文件出現bug之后&#xff0c;痛改前非&#xff0c;決定吃透Mapper.xml映射文件。 讓我們通過具體的代碼段來進一步理解 MyBatis 的 Mapper XML 文件中的每個組成部分。 <?xml version"1.0" encoding"UTF-8"?> <!…

python 爬取當當網圖書榜

首先查看當當網好評書單頁面&#xff0c;找到翻頁的URL參數 直接用requests請求頁面 resp requests.get(url) 找到想要的信息&#xff0c;使用正則表達式把這些信息提取出來 patternre.compile(list_num.*?(\d).<.*?<img src"(.*?)".*?title"(.*?…

Eel入門還有一些案例

Eel入門還有一些案例 Eel 是一個 Python 庫&#xff0c;它允許 Python 程序通過簡單的 API 與網頁進行交互。它使用 WebSocket 協議來實現 Python 后端和 JavaScript 前端之間的實時通信。下面是關于 Eel 的用法、通信原理和使用場景的一篇博客文章。 Eel的基本原理 Eel的基本原…

針對vue3的render函數添加自定義指令

話不多說 直接上代碼 主要是給h函數設置自定義指令控制 import /styles/reset.css import /styles/global.scss import uno.cssimport { createApp } from vue import App from ./App.vue import { setupRouter } from ./router import { setupStore } from ./store import …

Android studio之編譯提示Could not find :umeng-asms-v1.2.1

1 、問題 Could not determine the dependencies of task :app:compileDebugJavaWithJavac. > Could not resolve all task dependencies for configuration :app:debugCompileClasspath.> Could not find :umeng-asms-v1.2.1:.Required by:project :app> Could not …

FGF14:腦部疾病新潛力靶標

成纖維細胞生長因子14&#xff08;FGF14&#xff09;是FGF11亞家族成員&#xff0c;在神經元的所有基本特性&#xff08;內在放電、興奮性和抑制性神經元的突觸傳遞和可塑性&#xff09;中發揮作用。 &#xff08;數據來源AlphaFold&#xff09; FGF14由247個氨基酸組成&#x…

實戰篇(九):解鎖3D魔方的秘密:用Processing編程實現交互式魔方

解鎖3D魔方的秘密:用Processing編程實現交互式魔方 使用 Processing 創建一個 3D 魔方效果展示1. 安裝 Processing2. 項目結構3. 代碼實現4. 代碼解釋4.1. 初始化魔方4.2. 繪制魔方4.3. 處理鼠標事件4.4. 檢查點擊的面4.5. 旋轉面和最終確定旋轉5. 運行和測試6. 細節解釋6.1. …

【資源調度】2-如何解決資源調度問題?

導讀&#xff1a;本期是全網最全【資源調度】系列推文的第2期(共50期左右)。上期我們在《何為調度&#xff1f;》中&#xff0c;對調度的定義與作用、計劃與調度的關系、調度問題的拆解做了詳細介紹。從本期開始&#xff0c;我們選擇【客服調度】場景作為【資源調度】問題的具象…