笛卡尔树
笛卡尔树的节点具有两个属性:键值与权值。
中序遍历每个节点,则其键值递增。任意一个父亲的权值都一定大于(小于)其儿子的权值。
由此可知,笛卡尔树的键值具有二叉搜索树的性质,权值具有堆的性质。Treap就是实现了一棵笛卡尔树。
笛卡尔树一般是根据序列建立的,一般以序列下标为键值,序列中的数为权值。
它的建立与虚树相似,从左到右将序列中的点依次插入树中,维护树的右链即可轻松实现。
一般来说,笛卡尔树中的一个点代表的是一段位置,这段位置就是中序遍历这个点的子树得到的那段连续区间(下面叫它管辖范围),在DP中也可能代表当前这个位置所在的极大矩形。
下面是几个简单的应用:
1.[POJ2559]Largest Rectangle in a Histogram
给一张图求出其中最大的矩形。
这是一道单调栈模板题,考虑笛卡尔树的做法。
显然答案就是每个点的管辖范围宽度乘上这个点的权值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 }