zoukankan      html  css  js  c++  java
  • 笛卡尔树

    对于一个序列建立的笛卡尔树满足:

    • 它是个二叉树。

    • 若以下标为关键字,笛卡尔树满足二叉搜索树性质(若以每个点一下都包含一段连续的区间)。

    • 若以权值为关键字,它是一个小根堆。

    • 若权值互不相同,那么这个序列的笛卡尔树唯一(当然,若权值相同,就会有很多棵树,于是就有笛卡尔树计数啦)。

    它具有这么多优秀的性质,当然可以干很多事情啦。

    (以上图片来自维基百科)

    建立

    维护一个栈维护从队列第一个元素到现在的递增子序列(存的当然是下标),接下来分类讨论:

    • 若新增元素大于等于栈顶,那么直接进栈并与接到此时栈顶右儿子。

    • 否则一直弹栈直到小于栈顶,将最后弹出的元素连到当前元素的左儿子,之后进站并连边栈顶。

    sta[rt=tp=1]=1;
    for(int i=2;i<=n;i++)
    {
    	 if(a[i]>=a[sta[tp]]) ch[sta[tp]][1]=i,sta[++tp]=i;
    	 else
    	 {
    	 	 while(a[i]<a[sta[tp]]) tp--;
    	 	 if(!tp) rt=i;
    	 	 ch[i][0]=sta[tp+1];
    	 	 ch[sta[tp]][1]=i;
    	 	 sta[++tp]=i;
    	 }
    }
    

    笛卡尔树计数

    笛卡尔树满足根节点的的权值小于等于儿子节点的权值。

    那么对于给定的一个数列,这个序列中所有的最小值一定都直接连接在根节点上。

    设有 (m) 个最小值,则这些节点可以组成的二叉树个数……

    就是卡特兰数的第 (m) 项啦!

    如上图,将最小值提取出后,可以将原序列分成若干段,分别求解最小值即可。

    $ exttt{code}$
    #define Maxn 1000005
    #define Maxpown 21
    #define mod 1000000007
    typedef long long ll;
    inline int rd()
    {
    	 int x=0;
         char ch,t=0;
         while(!isdigit(ch = getchar())) t|=ch=='-';
         while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
         return x=t?-x:x;
    }
    int n;
    int st[Maxn][Maxpown],pos[Maxn][Maxpown];
    int pow2[Maxpown],lg[Maxn];
    ll Cart[Maxn],inv[Maxn<<1],mi[Maxn<<1],invmi[Maxn<<1];
    inline ll C(int x,int y) { return mi[x]*invmi[x-y]%mod*invmi[y]%mod; }
    inline ll ksm(ll x,ll y)
    {
    	 ll ret=1;
    	 while(y)
    	 {
    	 	 if(y&1) ret=ret*x%mod;
    	 	 x=x*x%mod,y>>=1;
    	 }
    	 return ret;
    }
    inline int query_pos(int l,int r)
    {
    	 int p=lg[r-l+1]-1;
    	 if(st[l][p]<=st[r-pow2[p]+1][p]) return pos[l][p];
    	 return pos[r-pow2[p]+1][p];
    }
    inline int query_min(int l,int r)
    {
    	 int p=lg[r-l+1]-1;
    	 return min(st[l][p],st[r-pow2[p]+1][p]);
    }
    ll Find(int l,int r)
    {
    	 if(l>r) return 1;
    	 ll ret=1;
    	 int Last=l,fir,cnt=0,Least=query_min(l,r),Now;
    	 while(Last<=r)
    	 {
    	 	 Now=query_min(Last,r);
    	 	 if(Now>Least) { ret=ret*Find(Last,r); break; }
    	 	 fir=query_pos(Last,r);
    	 	 ret=ret*Find(Last,fir-1)%mod;
    	 	 Last=fir+1,cnt++;
    	 }
    	 ret=ret*Cart[cnt]%mod;
    	 return ret;
    }
    int main()
    {
    	 n=rd(),pow2[0]=1;
    	 for(int i=1;i<=n;i++) st[i][0]=rd(),pos[i][0]=i;
    	 inv[0]=mi[0]=invmi[0]=inv[1]=mi[1]=invmi[1]=1;
    	 for(ll i=2;i<=n+n;i++)
    	 {
    	 	 mi[i]=mi[i-1]*i%mod;
    	 	 inv[i]=(mod-mod/i)*inv[mod%i]%mod;
    	 	 invmi[i]=invmi[i-1]*inv[i]%mod;
    	 }
    	 for(int i=1;i<=n;i++) lg[i]=lg[i>>1]+1;
    	 for(ll i=1;i<=n;i++) Cart[i]=C(i+i,i)*ksm(i+1,mod-2)%mod;
    	 for(int i=1;i<=20;i++) pow2[i]=pow2[i-1]*2;
    	 for(int i=1;i<=20;i++)
    	 	 for(int j=1;j<=n-pow2[i]+1;j++)
    	 	 {
    	 	 	 if(st[j][i-1]<=st[j+pow2[i-1]][i-1])
    	 	 	 	 pos[j][i]=pos[j][i-1];
    	 	 	 else pos[j][i]=pos[j+pow2[i-1]][i-1];
    	 	 	 st[j][i]=min(st[j][i-1],st[j+pow2[i-1]][i-1]);
    		 }
    	 printf("%lld
    ",Find(1,n));
         return 0;
    }
    

    直方图最大子矩形

    这道题可以用单调栈,也可以用笛卡尔树!

    一个笛卡尔树上的点 (x) 以及其子树表示大于等于 (val(x)) 的整个区间。

    所以 (ans=max{val(x) imes siz(x)})

    $ exttt{code}$
    #define Maxn 100005
    int n,tp,rt;
    ll ans;
    int a[Maxn],siz[Maxn],sta[Maxn];
    int ch[Maxn][2];
    inline void build()
    {
    	 sta[rt=tp=1]=1,ans=0;
    	 for(int i=2;i<=n;i++)
    	 {
    	 	 if(a[i]>=a[sta[tp]]) ch[sta[tp]][1]=i,sta[++tp]=i;
    	 	 else
    	 	 {
    	 	 	 while(a[i]<a[sta[tp]]) tp--;
    	 	 	 if(!tp) rt=i;
    	 	 	 ch[i][0]=sta[tp+1];
    	 	 	 ch[sta[tp]][1]=i;
    	 	 	 sta[++tp]=i;
    		 }
    	 }
    }
    void dfs(int x)
    {
    	 siz[x]=1;
    	 for(int i=0;i<2;i++) if(ch[x][i])
    	 	 dfs(ch[x][i]),siz[x]+=siz[ch[x][i]];
    	 ans=maxll(ans,1ll*a[x]*siz[x]);
    }
    int main()
    {
    	 while((n=rd())!=0)
    	 {
    	 	 for(int i=1;i<=n;i++) a[i]=rd(),ch[i][0]=ch[i][1]=0;
    	 	 build(),dfs(rt);
    	 	 printf("%lld
    ",ans);
    	 }
    	 return 0;
    }
    
  • 相关阅读:
    BZOJ 3218: a + b Problem
    P4542 [ZJOI2011]营救皮卡丘
    P4843 清理雪道
    P4553 80人环游世界
    P4126 [AHOI2009]最小割
    P2619 [国家集训队2]Tree I
    P2469 [SDOI2010]星际竞速
    P2050 [NOI2012]美食节
    易语言入门
    jdbc连接oracle语法
  • 原文地址:https://www.cnblogs.com/EricQian/p/15495954.html
Copyright © 2011-2022 走看看