這篇題解主要講**實現,思路可能需要照著**理解,請慎重閱讀
兩張掛了,重新傳了一遍。
最後**寫出來3.5kb,不到150行,相對來說還是挺短的...
給定一棵 \(n\) 個節點的樹,邊帶權,編號 \(0 \sim n-1\),需要支援五種操作:
保證任意時刻所有邊的權值都在 \([-1000,1000]\) 內。
前置知識:樹鏈剖分。
主要講**實現。
首先因為樹的編號不好看,所以都加一。
然後分操作考慮。
陣列定義寫在前面:
dep:深度
fa:父親結點
sz:子樹大小
son:重兒子
id:結點對應的dfs序
rk:dfs序對應的結點
id[i]:用來承接 [陣列中儲存的編號為id 的邊] 的資訊的點編號
a[u]:點u承接下來的資訊
top:重鏈頂端
線段樹結點(動態開點):
part 0 整體思路
因為傳統的重鏈剖分是把資訊對於點來儲存,對於邊考慮怎麼算。
於是就有乙個很樸素的想法,把每條邊的邊權下沉到對應的點。
也就是像下面這樣,用u號點來承接邊(f→u)的權值。
因為輸入的邊不一定是從父親到兒子,所以我們考慮把輸入的第 \(i\) 條邊記錄到陣列裡第 \(2i,2i+1\) 的位置。
這樣我們在重鏈剖分dfs的時候,假設通過點 \(u\) 走到了 \(v=to[i]\) ,那麼我們就承接邊權到 \(v\) ,即a[v]=val[i]
,同時為了方便修改邊權落實到修改點權,記錄id[i/2]=v
。
然後考慮這樣會對查詢操作有什麼影響,顯然如下圖:
我們要求 \(u,v\) 之間的邊權資訊,但是常規的重鏈剖分會把上面打×的邊也統計。
這個解決就考慮樹鏈剖分查詢操作的時候,最終會有一段對於id[x],id[y]
的查詢 (id[x]於是這個問題也解決了。就可以開始幹**了。
先有三個基礎的函式放在這裡。
void up(int k)
void down(int k)
void opp(int k)
分別是線段樹的push_up,push_down
和把結點 \(k\) 整體取反。
這裡說下整體取反。顯然就是新的最小值等於原來最大值的相反數,最大值同理。權值和就等於之前的相反數。
注意:這裡的 \(f\) 是還沒有傳下去的標記,不能抵消將要打在自己身上的取反操作。
part 1 單點賦值
這個是基礎線段樹操作,輸入把邊 \(u\) 賦值為 \(w\) ,那就把線段樹上的id[id[u]]
賦值成 \(w\) 。
void modify(int k,int l,int r,int pos,int x)
part 2 兩點路徑取相反數
先是常規套路,把線段樹上的一段給取相反數。
這個顯然也是基礎的線段樹的操作。
void md(int k,int l,int r,int x,int y)
然後也是重鏈剖分的板子。
注意這裡的if(x==y)return;
,因為如果最後x,y重合,表示它們都是輸入的x,y的lca,那麼貢獻就不能記錄到答案裡。
void modi(int x,int y)
part 3 三種查詢
發現這三種查詢本質相同。
於是**基本就和樹鏈剖分板子一樣了。
void ask_xds(int k,int l,int r,int x,int y,int op)
down(k);
int m=l+r>>1;
if(x<=m)ask_xds(ls,l,m,x,y,op);
if(y>m)ask_xds(rs,m+1,r,x,y,op);
up(k);
}int ask(int x,int y,int op)
完整**lin P1505 國家集訓隊 旅遊
題目鏈結 這道題其實還是比較好想的,同樣是邊權問題。我們需要維護最大值,最小值,和。最坑的地方就是路徑上的所有數變相反數,其實這個就是把區間和 1,區間最大 1,區間最小 1,最後pushdown的時候將取反標記 1,接下來一系列都是常規操作。而這裡還要記住,單點修改時也要下傳lazy標記。而我在跳...
P1505 國家集訓隊 旅遊
ray 樂忠於旅遊,這次他來到了t 城。t 城是乙個水上城市,一共有 n 個景點,有些景點之間會用一座橋連線。為了方便遊客到達每個景點但又為了節約成本,t 城的任意兩個景點之間有且只有一條路徑。換句話說,t 城中只有n 1 座橋。ray 發現,有些橋上可以看到美麗的景色,讓人心情愉悅,但有些橋狹窄泥...
洛谷 P1505 國家集訓隊 旅遊
洛谷傳送門 ray 樂忠於旅遊,這次他來到了 t 城。t 城是乙個水上城市,一共有 nn 個景點,有些景點之間會用一座橋連線。為了方便遊客到達每個景點但又為了節約成本,t 城的任意兩個景點之間有且只有一條路徑。換句話說,t 城中只有 n 1n 1 座橋。ray 發現,有些橋上可以看到美麗的景色,讓人...