笛卡爾樹學習筆記

2022-05-20 11:52:06 字數 4292 閱讀 5525

笛卡爾樹學習筆記

笛卡爾樹是一種二叉樹,每乙個結點由乙個鍵值二元組 \((k,w)\)構成。要求 \(k\)滿足二叉搜尋樹的性質,而 \(w\)滿足堆的性質。乙個有趣的事實是,如果笛卡爾樹的\(k,w\) 鍵值確定,且\(k\)互不相同,\(w\)互不相同,那麼這個笛卡爾樹的結構是唯一的

在一般情況下,未說明\(k\)時,我們預設\(k\)為下標。

下圖是對於序列\([9,3,7,1,8,12,10,20,15,18,5]\)的笛卡爾樹構造。

關於笛卡爾樹的構造我們一般都用單調棧來實現,時空複雜度均為\(o(n)\)。

rep(i,1,n)
我們先對於所有的元素按照鍵值\(k\)進行排序,這樣我們可以保證笛卡爾樹二叉搜尋樹的性質。

然後,對於每個元素我們依次插入到笛卡爾樹中去,那麼每次我們插入的元素都在這個樹的右鏈的末端。

於是我們執行這樣乙個過程,從下往上比較右鏈結點與當前結點\(u\)的\(w\),如果找到了乙個右鏈上的結點 \(w_x\)滿足 \(w_x ,就把 \(u\) 接到 \(x\) 的右兒子上,而 \(x\) 原本的右子樹就變成 \(u\) 的左子樹,而這一過程我們可以利用單調棧直接進行維護。

hdu-1506 largest rectangle in a histogram

題意:給出了\(n\)個寬度為\(1\),高度不同的矩陣,讓我們求其中的最大子矩陣。

這道題一眼就是單調棧裸題了,然而我們在這裡要用笛卡爾樹去求解。

首先,我們發現這道題非常符合笛卡爾樹的性質;

1.我們以下標為\(k\)的話,乙個子樹所對應的是一段區間。

2.乙個子樹內所有的矩形高度都\(>=\)其根節點的高度。

既然這樣的話,我們建完笛卡爾樹後直接\(dfs\)一遍,每個節點的最大子矩陣就是\(高度*其子樹的大小\)。

**如下

#include using namespace std;

#define int long long

#define reg register

#define raed read

#define clr(a,b) memset(a,b,sizeof a)

#define mod(x) (x>=mod)&&(x-=mod)

#define debug(x) cerr<

#define erep(i,g,x) for(int i=(g).head[x]; i; i=(g).nxt[i])

inline int read(void)

templateinline bool min(t &a, t const&b)

templateinline bool max(t &a, t const&b)

ans=0;

dfs(stk[1]);

printf("%lld\n",ans); }}

signed main()

有了這道題的啟示之後,我們來看一下這道題。

[bzoj2616]spoj periodni

題意:即給出\(n\)個\(1*h_i\)的矩陣,在一條直線上對齊下表面,求放置\(k\)個互不攻擊的車的方案數。

顯然是先建出小根笛卡爾樹,考慮每個矩形內部的答案。

設 \(dp[u][i]\) 表示 \(u\) 子樹內放 \(i\) 個數的方案數, \(dp1[i]\) 表示 當前子樹\(u\)內不考慮當前矩形,放 \(i\) 個數的方案數,設\(h[i]\)為當前矩陣可行高度(即\(a[fa[u]]-a[u]\))。

顯然有 \(dp1 = f[ls]*f[rs]\),即左右子樹的卷積。

接下來就是揹包的轉移了,列舉當前矩形內有多少列還是空的進行轉移。

設當前子樹放置\(i\)個棋子,有\(j\)個在當前矩陣放置。

則\(dp[u][i]+=\sum_^idp1[i-j]*c(sz[u]-(i-j),j)*c(h[x],j)*j!\)

第乙個組合數是列舉矩陣所剩的行,第二個組合數是列舉矩陣所剩的列。

最後乘上 j! 是因為橫縱座標是兩兩組合的,因此匹配的方案數為 j!。

#include using namespace std;

#define int long long

#define reg register

#define raed read

#define clr(a,b) memset(a,b,sizeof a)

#define mod(x) (x>=mod)&&(x-=mod)

#define debug(x) cerr<

#define erep(i,g,x) for(int i=(g).head[x]; i; i=(g).nxt[i])

inline int read(void)

templateinline bool min(t &a, t const&b)

templateinline bool max(t &a, t const&b)

rep(i,0,1)rep(j,1,n)if(son[j][i])h[son[j][i]]=a[son[j][i]]-a[j];

h[stk[1]]=a[stk[1]];

dfs(stk[1]);

printf("%d\n",dp[stk[1]][k]);}

signed main()

我們發現對於這種題目,建完笛卡爾樹後的樹形\(dp\)我們一般都要有乙個不考慮當前矩陣的\(dp\)轉移陣列\(dp1\)。

因為這樣我們就可以將所有的情況給加到當前矩陣的可行區域中去。

同時一般來說\(dp1\)都是當前子樹的左右子樹的卷積\(dp[ls]*dp[rs]\)。

這裡還有乙個有關笛卡爾樹的用法:rmq

[bzoj5042]lwd的分科島

沒錯,笛卡爾樹可以用來解\(rmg\)問題,我們對於每次查詢區間進行離線。

首先,我們可以根據最大或最小建出大根堆或者是小根堆,

乙個區間的最值就是其左右端點\(lca\)的值,利用離線\(lca\)我們可以做到時間複雜度為\(o(n*\alpha_n)\)的優秀複雜度。

此題嚴重卡常!!!!

**如下:

#include using namespace std;

#define reg register

#define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)

#define erep(i,x) for(int i=head[x]; i; i=nxt[i])

char u[20000],*p1=u,*p2=u;

inline int read(void)

const int n=3e6+5,m=1500005,mod=1e9+7;

int n,q,stk[n],a[n],ans[m],tot,head[n],to[n],nxt[n],cost[n],lc[n],rc[n],fa[n],mark[n];

struct query b[n];

inline void addedgepair(int a,int b,int c)

int find(int x){return x==fa[x]?fa[x]:fa[x]=find(fa[x]);}

void dfs(int x)

}char buff[20000000],*iter=buff,stk[15];

void _main(void)

while(top>1)rc[stk[top-1]]=stk[top],top--;

rep(i,1,q)if(b[i].op==1)addedgepair(b[i].l,b[i].r,i);

rep(i,1,n)fa[i]=i;

dfs(root);

tot=0;

rep(i,1,n)fa[i]=i,lc[i]=rc[i]=mark[i]=head[i]=0;

stk[top=1]=root=1;

rep(i,2,n)

while(top>1)rc[stk[top-1]]=stk[top],top--;

rep(i,1,q)if(b[i].op==2)addedgepair(b[i].l,b[i].r,i);

dfs(root);

rep(i,1,q)

*iter++='\n';

} fwrite(buff,1,iter-buff,stdout);

}signed main()

pat笛卡爾樹

笛卡爾樹是一種特殊的二叉樹,其結點包含兩個關鍵字k1和k2。首先笛卡爾樹是關於k1的二叉搜尋樹,即結點左子樹的所有k1值都比該結點的k1值小,右子樹則大。其次所有結點的k2關鍵字滿足優先佇列 不妨設為最小堆 的順序要求,即該結點的k2值比其子樹中所有結點的k2值小。給定一棵二叉樹,請判斷該樹是否笛卡...

笛卡爾樹小結

粗略的學習了一下笛卡爾樹 主要是為了平衡樹打基礎吧 因為關於平衡樹 treap 早忘了 splay 不信任複雜度 然後 我能學一種比較簡單的樹y 笛卡爾樹.這裡以建出小根堆為例。描述區間性質的樹 可以當成二叉搜尋樹不過並不平衡因為每次都是選取當前區間最小值當做為根 然後顯然根據區間的數的排列不同樹的...

笛卡爾 關於笛卡爾

陪孩子看書看到笛卡爾,突然想到了笛卡爾積以及cross join等等,無法忽視的數學之美。關於笛卡爾發明座標系還有這樣乙個故事 有一天,笛卡爾 1596 1650,法國哲學家 數學家 物理學家 生病臥床,但他頭腦一直沒有休息,在反覆思考乙個問題 幾何圖形是直觀的,而代數方程則比較抽象,能不能用幾何圖...