主席樹學習筆記

来源:https://www.cnblogs.com/OIerBoy/archive/2023/08/26/17410228.html
-Advertisement-
Play Games

# 什麼是主席樹 主席樹這個名字看上去很高級,其實不然,它還有另一個名字——可持久化線段樹。 ## 什麼是可持久化 可持久化顧名思義就是它可以變得~~**持久**~~,就是我們對他不斷進行單點修改後,突然查詢它的某一個歷史版本,這就叫可持久化。 # 引入例題 [洛谷3919:可持久化數組](http ...


什麼是主席樹

主席樹這個名字看上去很高級,其實不然,它還有另一個名字——可持久化線段樹。

什麼是可持久化

可持久化顧名思義就是它可以變得持久,就是我們對他不斷進行單點修改後,突然查詢它的某一個歷史版本,這就叫可持久化。

引入例題

洛谷3919:可持久化數組

題目大意

如題,你需要維護這樣的一個長度為 \(N\ (1\le N\le 10^6)\) 的數組,支持如下幾種操作

  • 1.在某個歷史版本上修改某一個位置上的值

  • 2.訪問某個歷史版本上的某一位置的值

此外,每進行一次操作(對於操作 2,即為生成一個完全一樣的版本,不作任何改動),就會生成一個新的版本。版本編號即為當前操作的編號(從 1 開始編號,版本 0 表示初始狀態數組)

此時我們就需要用到主席樹了。

分析

問題:這裡我們為什麼能直接用數組來做?

很簡單,如何我們用數組來做的話每改變一個數,我們就要新建一個數組並將其他沒有改變的也一起複制下來,而\(1\le N \le 10^6\) 空間不支持(如果可以就沒必要做了)

思考:問什麼明明只修改了一個數,卻必須將其他的數也複製一遍?

那是因為數組的一級索引所決定的。

什麼是索引

索引是一種單獨的、物理的對資料庫表中一列或多列的值進行排序的一種存儲結構,它是某個表中一列或若幹列值的集合和相應的指向表中物理標識這些值的數據頁的邏輯指針清單。

索引的作用相當於圖書的目錄,可以根據目錄中的頁碼快速找到所需的內容。

最優索引

問題:怎樣的索引是最高效呢?

這裡,我們設索引大小為 \(k\),那麼索引的層數為 \(\log_kn\),則每次修改的數量為 \(k\log_kn\)

\(k\log_kn=\log_kn^k=k\dfrac{\log n}{\log k}=\log n\dfrac{k}{\log k}\)

\(f(n)=\dfrac{k}{\log k}\)

\(f'(n)=\dfrac{\log k+1}{\log^2 k}=\left(\dfrac{1}{\log k}\right)^2+\dfrac{1}{\log k}\)

\(1\le k\le n\) 的情況下,\(\log k\in\left[0,\infty\right]\)

所以 \(k_{\min}=e\ (\log k=1)\)

即索引為 \(k=2\)\(k=3\) 時最優。

這裡我們取 \(k=2\),因為它可以用線段樹維護。

演算法

原理

我們想要支持回退操作就需要對每一次修改操作都進行一次複製,將一些未進行操作也進行複製,這樣就可以訪問到舊版本的線段樹了。

那我們來分析一下單點修改時那些需要複製:

image

所以每一次修改我們只需要修改 \(\log n\) 個點,像這樣:

image

每一次都只修改被影響的點就可以。

從這張圖中,我們就發現主席樹的一些性質:

  • 增加的非葉結點的兒子一個是其他版本的節點,一個是新節點。

  • 主席樹有很多根

  • 對於每一個根下麵都是一棵完整的線段樹

  • 節點都有可能有很多父節點

具體實現

  • 每次增加新節點直接開一塊空間新節點,編號為總節點數個數+1

  • 用結構體來存子節點編號

  • 訪問子節點時,不是像線段樹一樣乘2或乘2+1,而是在結構體存子節點編號

  • 每次新開個數組存根。

模版代碼(P3919)

#include<bits/stdc++.h>
#define Mod 1000000007
#define int long long
#define For(i,j,k) for(int i=j;i<=k;++i)
#define FOR(i,j,k) for(int i=j;i>=k;i--)
#define mid ((l+r)>>1)
#define N 1000006
using namespace std;
int a[N],n,m,Q,root[N<<5];
struct PST{
	int lc[N<<5],rc[N<<5],val[N<<5],cnt;
	void build(int &x,int l,int r){
		x=++cnt;
		if(l==r){
			val[x]=a[l];
			return ;
		}
		build(lc[x],l,mid);
		build(rc[x],mid+1,r);
	}
	void ins(int &x,int k,int l,int r,int L,int R){
		x=++cnt;
		lc[x]=lc[k];
		rc[x]=rc[k];
		val[x]=val[k];
		if(l==r){
			val[x]=R;
			return ;
		}
		if(L<=mid)ins(lc[x],lc[k],l,mid,L,R);
		else ins(rc[x],rc[k],mid+1,r,L,R);
	}
	int query(int x,int l,int r,int k){
		if(l==r)return val[x];
		if(k<=mid)return query(lc[x],l,mid,k);
		else return query(rc[x],mid+1,r,k);
	}
}T;
signed main(){
	cin>>n>>m;
	For(i,1,n)cin>>a[i];
	T.build(root[0],1,n);
	For(i,1,m){
		int k,opt,x,y;
		cin>>k>>opt>>x;
		if(opt==1){
			cin>>y;
			T.ins(root[i],root[k],1,n,x,y);
		}
		if(opt==2){
			cout<<T.query(root[k],1,n,x)<<endl;
			root[i]=root[k];
		}
	}
	return 0;
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 使用的依賴:Apache提供的poi包 首先導入依賴 <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.2</version> </dependency> 核心 ...
  • 問題代碼: 1 #include<windows.h> 2 #include<iostream> 3 #include<thread> 4 HANDLE h1; 5 HANDLE h2; 6 7 void CALLBACK test(PVOID a, BOOLEAN b) 8 { 9 std::co ...
  • ## IO 神器 Okio [官方](https://square.github.io/okio/) 是這麼介紹 Okio 的: > Okio is a library that complements java.io and java.nio to make it much easier to a ...
  • Python編程語言允許我們執行各種任務,所有這些都是在簡單模塊和短小精悍的代碼的幫助下完成的。在Python的幫助下進行屏幕截圖就是這樣一項任務。 Python為我們提供了許多模塊,使我們能夠執行不同的任務。有多種方法可以使用Python及其庫進行屏幕截圖。 ### 用Pyautogui模塊進行截 ...
  • 顧名思義,Python中的自動點擊器是一個簡單的Python應用程式,可以按照用戶的要求重覆點擊滑鼠。不同的參數,如速度、頻率和位置,可以根據用戶的要求進行改變。 Python有不同的模塊可用於控制鍵盤、滑鼠等設備。因此,我們可以使用這些模塊在Python中輕鬆創建一個自動點擊器。 本教程將展示在P ...
  • [TOC] spdlog是一個開源、跨平臺、無依賴、只有頭文件的C++11日誌庫,網上介紹的文章有很多這裡就不過多的介紹了,GitHub鏈接:[https://github.com/gabime/spdlog](https://github.com/gabime/spdlog)。 # 引用源碼 先下 ...
  • `fsnotify`是一個用Go編寫的文件系統通知庫。它提供了一種觀察文件系統變化的機制,例如文件的創建、修改、刪除、重命名和許可權修改。它使用特定平臺的事件通知API,例如Linux上的inotify,macOS上的FSEvents,以及Windows上的ReadDirectoryChangesW。 ...
  • 工具提示即 Tool Tip,當用戶把滑鼠移動到某個UI對象上並懸停片刻,就會出現一個“短小精悍”的視窗,顯示一些說明性文本。一般就是功能描述,讓用戶知道這個XX是幹啥用的。 在 Qt 中使用工具提示最方便的做法是直接用 QWidget 類的成員方法:setToolTip。從 QWidget 類派生 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...