發表文章

目前顯示的是有「笛卡爾樹」標籤的文章

[TIOJ] 2027. 腳步鬆散

題目連結: https://tioj.infor.org/problems/2027 不知道多久沒來這裡了XD,來提供一下兩個這題的做法。 一個是gamegame跟我講的做法,我覺得我應該以前都沒想過,所以紀錄一下這個有趣的作法。 這裡我們將一棵二元樹的節點集合用$\mathbb{T}$表示,並且定義幾個函數 $\text{lch}:\mathbb{T}\rightarrow\mathbb{T}, \text{lch}(x)=\text{left child of }x$ $\text{rch}:\mathbb{T}\rightarrow\mathbb{T}, \text{rch}(x)=\text{right child of }x$ $\text{sz}:\mathbb{T}\rightarrow\mathbb{N}\cup\{0\}, \text{sz}(x)=\text{size of }x$ $\text{w}:\mathbb{T}\rightarrow\mathbb{N}\cup\{0\}, \text{w}(x)=\min(\text{sz}(\text{lch}(x)), \text{sz}(\text{rch}(x)))$ 那首先有個引理:對於任意二元樹$T$都有$\displaystyle\sum_{u \in T}\text{w}(u) = \mathcal{O}(\text{sz}(T) \log \text{sz}(T))$,證明的部分就留給讀者好了(X) 白話文講一點就是如果有一個$N$個節點的二元樹,而我們對於他每個節點作的演算法都只跟那個節點小的子樹的大小有關的話,複雜度就會是$\mathcal{O}(N \log N \cdot \text{單一操作的複雜度} )$。 回到這題,可以發現如果一個區間$[L, R]$的最大值是$a_i$若且唯若$L \in [j, i]$(其中$j$是使得$j a_i$發生的最大的$j$)跟$R \in [i, j]$(其中$j$是使得$i a_i$發生的最小的$j$),而若是我們把這種性質拿去建成一棵二元樹(也就是讓可能的編號$L$都在$i$的左子樹,可能的編號$R$都在$i$的右子樹)的話我們就會拿到 笛卡爾樹 。 有了笛卡爾樹後,我們還需要一個支援插入、刪除、統計有多...

[TIOJ] 1332. 名義老爸

題目連結: http://tioj.infor.org/problems/1332 可以發現對於每個點的爸爸必定是在他出現前出現過的,且必定是比他大的最小值或比他小的最大值中較後面插入的數,不過詳細的證明我不太會證...如果有大大願意教我一下我洗耳恭聽。 因此就用個map存每個數,因為要快速找到比他大的最小值跟比他小的最大值,所以不妨把值當作key,插入順序當作value,就得到了一個$O(N log N)$的做法了。 不過本題其實還有更快的做法,複雜度可變成$O(N)$,那就是對於這個數列用奇怪的方式(?建出一顆笛卡爾樹,再轉回原數列即可知道答案,雖然你還是會構造出整棵樹,但因為笛卡爾樹有線性的建法,所以複雜度還是$O(N)$ 詳細可參考: 這篇 (我覺得比較好理解在幹嘛) 跟 這篇 有講怎麼線性構造出笛卡爾樹 #include <bits/stdc++.h> using namespace std; #define N 1000000 int ans[N+5]; map<int,int> mp; int main(){ ios_base::sync_with_stdio(0);cin.tie(0); int n;cin>>n; for(int i=1;i<=n;++i){ int x;cin>>x; int l=-1,r=-1,ll,rr; auto it = mp.insert({x,i}).first; ++it; if(it!=mp.end()){ l=it->second; ll=it->first; } --it; if(it!=mp.begin()){ --it; r=it->second; rr=it->fir...