笛卡爾樹的節點具備兩個屬性:鍵值與權值。ide
中序遍歷每一個節點,則其鍵值遞增。任意一個父親的權值都必定大於(小於)其兒子的權值。spa
由此可知,笛卡爾樹的鍵值具備二叉搜索樹的性質,權值具備堆的性質。Treap就是實現了一棵笛卡爾樹。3d
笛卡爾樹通常是根據序列創建的,通常以序列下標爲鍵值,序列中的數爲權值。code
它的創建與虛樹類似,從左到右將序列中的點依次插入樹中,維護樹的右鏈便可輕鬆實現。blog
通常來講,笛卡爾樹中的一個點表明的是一段位置,這段位置就是中序遍歷這個點的子樹獲得的那段連續區間(下面叫它管轄範圍),在DP中也可能表明當前這個位置所在的極大矩形。get
下面是幾個簡單的應用:string
1.[POJ2559]Largest Rectangle in a Histogramit
給一張圖求出其中最大的矩形。io
這是一道單調棧模板題,考慮笛卡爾樹的作法。event
顯然答案就是每一個點的管轄範圍寬度乘上這個點的權值h[]的最大值。
1 #include<cstdio> 2 #include<algorithm> 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 4 typedef long long ll; 5 using namespace std; 6 7 const int N=100010; 8 ll ans; 9 int n,top,rt,a[N],w[N],stk[N],son[N][2]; 10 11 void dfs(int x,int L,int R){ 12 w[x]=R-L+1; ans=max(ans,1ll*w[x]*a[x]); 13 if (son[x][0]) dfs(son[x][0],L,x-1); 14 if (son[x][1]) dfs(son[x][1],x+1,R); 15 } 16 17 int main(){ 18 freopen("poj2559.in","r",stdin); 19 freopen("poj2559.out","w",stdout); 20 while (scanf("%d",&n),n){ 21 rep(i,1,n) scanf("%d",&a[i]); 22 top=0; 23 rep(i,1,n) son[i][0]=son[i][1]=0; 24 rep(i,1,n){ 25 while (top && a[stk[top]]>a[i]) son[i][0]=stk[top--]; 26 if (top) son[stk[top]][1]=i; 27 stk[++top]=i; 28 } 29 rt=stk[1]; ans=0; dfs(rt,1,n); printf("%lld\n",ans); 30 } 31 return 0; 32 }
2.[BZOJ5042]LWD的分科島
用LCA作到$O(n\alpha(n))$實現RMQ。
以求區間最小值爲例,先建出小根笛卡爾樹,當詢問[l,r]時,找到l,r對應的點,它們的LCA的權值就是[l,r]的最小值。
Tarjan離線實現LCA便可。
1 #include<cstdio> 2 #include<algorithm> 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 4 using namespace std; 5 6 const int N=3000010; 7 int n,m,cnt,top1,top2,rt1,rt2,op,l,r,h[2][N],val[N],to[N<<1],nxt[N<<1]; 8 int vis[N],a[N],stk1[N],stk2[N],fa[N],ans[N],son[2][N][2]; 9 10 int get(int x){ return fa[x]==x ? x : fa[x]=get(fa[x]); } 11 void add(int k,int u,int v,int w){ to[++cnt]=v; val[cnt]=w; nxt[cnt]=h[k][u]; h[k][u]=cnt; } 12 13 int rd(){ 14 int x=0; char ch=getchar(); bool f=0; 15 while (ch<'0' || ch>'9') f|=(ch=='-'),ch=getchar(); 16 while (ch>='0' && ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); 17 return f ? -x : x; 18 } 19 20 void Tarjan(int x,int k){ 21 vis[x]=1; 22 if (son[k][x][0]) Tarjan(son[k][x][0],k),fa[son[k][x][0]]=get(x); 23 if (son[k][x][1]) Tarjan(son[k][x][1],k),fa[son[k][x][1]]=get(x); 24 for (int i=h[k][x]; i; i=nxt[i]){ 25 int z=to[i],id=val[i]; 26 if (vis[z]) ans[id]=a[get(z)]; 27 } 28 } 29 30 int main(){ 31 freopen("bzoj5042.in","r",stdin); 32 freopen("bzoj5042.out","w",stdout); 33 n=rd(); m=rd(); 34 rep(i,1,n) a[i]=rd(); 35 rep(i,1,n){ 36 while (top1 && a[stk1[top1]]>a[i]) son[0][i][0]=stk1[top1--]; 37 if (top1) son[0][stk1[top1]][1]=i; 38 stk1[++top1]=i; 39 while (top2 && a[stk2[top2]]<a[i]) son[1][i][0]=stk2[top2--]; 40 if (top2) son[1][stk2[top2]][1]=i; 41 stk2[++top2]=i; 42 } 43 rt1=stk1[1]; rt2=stk2[1]; 44 rep(i,1,m){ 45 op=rd(); l=rd(); r=rd(); 46 if (op==1) add(0,l,r,i),add(0,r,l,i); else add(1,l,r,i),add(1,r,l,i); 47 } 48 rep(i,1,n) fa[i]=i; Tarjan(rt1,0); 49 rep(i,1,n) fa[i]=i; Tarjan(rt2,1); 50 rep(i,1,m) printf("%d\n",ans[i]); 51 return 0; 52 }
3.[ICPC 2016 Hong Kong]G.Scanffolding
https://blog.gyx.me/note/cartesiantree.pdf
1 #include<cstdio> 2 #include<algorithm> 3 #define ls son[x][0] 4 #define rs son[x][1] 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 6 typedef long long ll; 7 using namespace std; 8 9 const int N=100010; 10 int n,m,top,a[N],son[N][2],stk[N]; 11 ll sm[N],f[N],g[N]; 12 13 int dfs(int x,int fa){ 14 if (!x) return 0; 15 int w=dfs(ls,x)+dfs(rs,x)+1; 16 sm[x]=a[x]+sm[ls]+sm[rs]; 17 ll r=1ll*(a[x]-a[fa])*w-(g[ls]+g[rs]); 18 f[x]=f[ls]+f[rs]+max(0ll,(r+m-1)/m); 19 g[x]=f[x]*m-(sm[x]-1ll*a[fa]*w); 20 return w; 21 } 22 23 int main(){ 24 freopen("scanffolding.in","r",stdin); 25 freopen("scanffolding.out","w",stdout); 26 scanf("%d%d",&n,&m); 27 rep(i,1,n) scanf("%d",&a[i]); 28 rep(i,1,n){ 29 while (top && a[stk[top]]>a[i]) son[i][0]=stk[top--]; 30 son[stk[top]][1]=i; stk[++top]=i; 31 } 32 dfs(stk[1],0); printf("%lld\n",f[stk[1]]); 33 return 0; 34 }
4.[BZOJ2616]SPOJ PERIODNI
與上題相似,搞清這個思想後DP方程就是很顯然的了。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=510,M=1000010,mod=1e9+7; 9 int n,m,tot,top,f[N][N],fac[M],inv[M],g[N],stk[N],fa[N],a[N],son[N][2]; 10 11 int C(int n,int m){ return n<m ? 0 : 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod; } 12 13 int ksm(int a,int b){ 14 int res=1; 15 for (; b; a=1ll*a*a%mod,b>>=1) 16 if (b & 1) res=1ll*res*a%mod; 17 return res; 18 } 19 20 void init(int n){ 21 fac[0]=inv[0]=1; 22 rep(i,1,n) fac[i]=1ll*fac[i-1]*i%mod; 23 inv[n]=ksm(fac[n],mod-2); 24 for (int i=n-1; i; i--) inv[i]=1ll*inv[i+1]*(i+1)%mod; 25 } 26 27 int dfs(int x){ 28 int s=1,b=a[x]-a[fa[x]]; f[x][0]=1; 29 rep(i,0,1) if (son[x][i]){ 30 int y=son[x][i],d=dfs(y); memset(g,0,sizeof(g)); 31 rep(j,0,s) rep(k,0,min(d,m-j)) g[j+k]=(g[j+k]+1ll*f[x][j]*f[y][k])%mod; 32 s+=d; rep(j,0,s) f[x][j]=g[j]; 33 } 34 for (int i=min(m,s); ~i; i--){ 35 int t=0; 36 rep(j,0,i) t=(t+1ll*f[x][i-j]*fac[j]%mod*C(s-i+j,j)%mod*C(b,j))%mod; 37 f[x][i]=t; 38 } 39 return s; 40 } 41 42 int main(){ 43 freopen("bzoj2616.in","r",stdin); 44 freopen("bzoj2616.out","w",stdout); 45 scanf("%d%d",&n,&m); init(1000000); 46 rep(i,1,n) scanf("%d",&a[i]); 47 rep(i,1,n){ 48 while (top && a[stk[top]]>a[i]) son[i][0]=stk[top--]; 49 if (top) son[stk[top]][1]=i,fa[i]=stk[top]; 50 stk[++top]=i; if (son[i][0]) fa[son[i][0]]=i; 51 } 52 dfs(stk[1]); printf("%d\n",f[stk[1]][m]); 53 return 0; 54 }