笛卡尔树

笛卡尔树的节点具有两个属性:键值与权值。

中序遍历每个节点,则其键值递增。任意一个父亲的权值都一定大于(小于)其儿子的权值。

由此可知,笛卡尔树的键值具有二叉搜索树的性质,权值具有堆的性质。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 }
POJ2559

 

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 }
BZOJ5042

 

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 }
Scanffolding

 

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 }
BZOJ2616

 

posted @ 2019-03-29 12:31  HocRiser  阅读(598)  评论(0编辑  收藏  举报