首页 > 其他分享 ><学习笔记> 笛卡尔树

<学习笔记> 笛卡尔树

时间:2024-02-06 15:00:32浏览次数:39  
标签:ch 笛卡尔 int rson st 节点

笛卡尔树是一种二叉树,每一个节点由键值二元组 \((k,w)\) 构成, \(k\) 满足二叉搜索树的性质, \(w\) 满足堆的性质。

构建

我们可以用一个栈进行构建,假如我们想要求 \(k\) 满足二叉搜索树的性质,那么我们首先需要按 \(k\) 从小到大排序,然后一个一个插入;假如我们想要 \(w\) 满足小根堆的性质,那么我们每回在栈中找到第一个大于该点的点然后将这个点接在那个点的右儿子上,然后将那个点原先的右儿子接在这个点左儿子上。

int lson[N+5],rson[N+5];
int st[N+5],top;
void build(){
    top=0;
    for(int i=1;i<=n;++i){
        int k=top;
        while(k && a[st[k]]>a[i]) k--;
        if(k) rson[st[k]]=i;
        if(k<top) lson[i]=st[k+1];
        st[++k]=i;
        top=k;
    }
}

应用

hdu6305 RMQ Similar Sequence

考虑到实数内随机不会重复,那么将这个序列考虑为一个排列。其实限制就是要求两棵笛卡尔树重构,那么重构的概率其实就是 \(\frac{1}{\prod_i siz_i}\) 相当于要求每个点是其子树中最大的点的概率。考虑权值是 \(\frac{n}{2}\) 所以答案就是相乘。感觉没啥意义

SPOJ PERIODNI

考虑建一棵权值为小根堆的笛卡尔树,这样的话,我们假如合并两个子树的答案就不会产生影响。我们设 \(dp_{i,j}\) 表示以 \(i\) 节点的子树中有 \(j\) 个互不相同点的方案数,然后发现转移就是一个树上的背包。我们再考虑这个节点贡献的计算,其实就是在一个长为 \(n\)(这个节点子树中最大编号与最小编号的差),宽为 \(m\) (这个节点的高度与父亲节点高度的差)的矩形中在排除已经选了的 \(x\) 个节点的情况下,选 \(k\) 个节点,方案其实就是 \({m \choose i}{n-x \choose i}i!\),然后就有转移:

\[f_{u,i+k} \leftarrow f_{u,i}*f_{v,k} \]

\[f_{u,i+k} \leftarrow f_{u,k}*{m \choose i}{n-x \choose i}i! \]

code
// #2616. SPOJ PERIODNI 
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1005;
const int M=1e6;
const int mod=1e9+7;
int n,k;
int a[N],dp[N][N],F[N];
int ls[N],rs[N],siz[N];
int lson[N],rson[N],st[N],top=0;
int fac[M+5],inv[M+5];
int qpow(int x,int p){
    int ans=1;
    while(p){
        if(p&1) ans=(ans*x)%mod;
        x=x*x%mod;
        p>>=1;
    }
    return ans;
}
void init(){
    fac[0]=inv[0]=1;
    for(int i=1;i<=M;i++) fac[i]=fac[i-1]*i%mod;
    inv[M]=qpow(fac[M],mod-2);
    for(int i=M-1;i>=1;i--) inv[i]=inv[i+1]*(i+1)%mod;
}
int C(int x,int y){
    if(x<y) return 0;
    return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
void build(){
    for(int i=1;i<=n;i++){
        int k=top;
        while(k && a[st[k]]>a[i]) k--;
        if(k) rson[st[k]]=i;
        if(k<top) lson[i]=st[k+1];
        st[++k]=i;
        top=k;
    }
}
void dfs(int x,int fa){
    ls[x]=rs[x]=x;
    siz[x]=1;
    dp[x][0]=1;
    if(lson[x]){
        dfs(lson[x],x);
        ls[x]=min(ls[x],ls[lson[x]]);
        rs[x]=max(rs[x],rs[lson[x]]);
        int y=lson[x];
        for(int i=0;i<=siz[x] && i<=k;i++){
            for(int j=0;i+j<=k && j<=siz[y];j++){
                F[i+j]=(F[i+j]+dp[x][i]*dp[y][j]%mod)%mod;
            }
        }
        siz[x]+=siz[y];
        for(int i=0;i<=siz[x];i++){
            dp[x][i]=F[i];
            F[i]=0;
        }
    }
    if(rson[x]){
        dfs(rson[x],x);
        ls[x]=min(ls[x],ls[rson[x]]);
        rs[x]=max(rs[x],rs[rson[x]]);
        int y=rson[x];
        for(int i=0;i<=siz[x] && i<=k;i++){
            for(int j=0;i+j<=k && j<=siz[y];j++){
                F[i+j]=(F[i+j]+dp[x][i]*dp[y][j]%mod)%mod;
            }
        }
        siz[x]+=siz[y];
        for(int i=0;i<=siz[x];i++){
            dp[x][i]=F[i];
            F[i]=0;
        }
    }
    int l=rs[x]-ls[x]+1;
    int r=a[x]-a[fa];
    for(int i=0;i<=siz[x] && i<=k;i++){
        for(int j=0;j<=siz[x] && i+j<=k && i+j<=siz[x];j++){
            F[i+j]=(F[i+j]+dp[x][i]*C(r,j)%mod*C(l-i,j)%mod*fac[j]%mod)%mod;
        }
    }
    for(int i=0;i<=siz[x];i++){
        dp[x][i]=F[i];
        F[i]=0;
    }
}
signed main(){
    init();
    scanf("%lld%lld",&n,&k);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    build();
    dfs(st[1],0);
    printf("%lld",dp[st[1]][k]);
}

hdu4125Moles

考虑其实就是建一棵二叉搜索树,我们考虑笛卡尔树 \(O(n)\) 构建,那么就是让权值满足二叉搜索树的性质,下表满足小根堆的性质。然后建完跑 \(kmp\) 就可以了。

code
// #2616. SPOJ PERIODNI 
#include<bits/stdc++.h>
using namespace std;
const int N=5*1e6;
int n,m,b[N];
struct asd{
    int w,id;
}a[N+5];
int st[N+5],top;
int lson[N+5],rson[N+5];
char s[N+5];
int dt[N*10],tp=0;
bool amp(asd a,asd b){
    return a.w<b.w;
}
void dfs(int x){
    dt[++tp]=b[x]%2;
    if(lson[x]){
        dfs(lson[x]);
        dt[++tp]=b[x]%2;
    }
    if(rson[x]){
        dfs(rson[x]);
        dt[++tp]=b[x]%2;
    }
}
void build(){
    top=0;
    for(int i=1;i<=n;i++){
        int k=top;
        while(k && st[k]>a[i].id) k--;
        if(k) rson[st[k]]=a[i].id;
        if(k<top) lson[a[i].id]=st[k+1];
        st[++k]=a[i].id;
        top=k;
    }
}
int nex[N+5];
void get_next(){
    nex[1]=0;
    int j=0;
    for(int i=2;i<=m;i++){
        while(j>0 && s[i]!=s[j+1] ) j=nex[j];
        if(s[i]==s[j+1]) j++;
        nex[i]=j;
    }
}
inline int read(){
	int x(0);bool f(0);char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar()) f^=ch=='-';
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
	return f?x=-x:x;
}
signed main(){
    int T;
    T=read();
    for(int p=1;p<=T;p++){
        int rt=0;
        tp=0;
        n=read();
        for(int i=1;i<=n;i++){
            b[i]=read();
            a[i].w=b[i];
            a[i].id=i;
        }
        scanf("%s",s+1);
        sort(a+1,a+n+1,amp);
        build();
        m=strlen(s+1);
        dfs(st[1]);
        get_next();
        int ans=0;
        int j=0;
        for(int i=1;i<=tp;i++){
            while(j && (dt[i]!=(s[j+1]-'0') || j==n )) j=nex[j];
            if(dt[i]==(s[j+1]-'0'))  j++;
            if(j==m) ans++;
        }
        printf("Case #%d: %d\n",p,ans);
        for(int i=0;i<=n;i++) lson[i]=rson[i]=0;
    }
}
/*
摩尔线不必按摩尔数排序。
每只鼹鼠都应该生活在自己挖的洞里,每只鼹鼠只挖一个洞。
这条线上的第一个鼹鼠挖第一个洞,洞里有一条通向地面的通道。
然后其他鼹鼠穿过那个通道,挖更多的洞和通道。
一个洞最多可以有三个由通道连接的邻居,一个在上层,另外两个洞在下层,分别位于左侧和右侧。
当鼹鼠到达一个洞时,如果它的数量小于洞主人的数量,它会去左下洞(或者挖一个左下洞,当没有左下洞时留在那里),
否则它会去右下洞(或挖一个右下洞,没有右下洞时呆在那里)。
由于其出色的能力和精心设计的布局,这些孔和通道不会相互交叉。
老鼠杰瑞是那些鼹鼠的朋友。
他来拜访他们,并为每一个鼹鼠准备礼物。
有一条规则是,数量较小的鼹鼠必须比数量较大的鼹鼠更早得到礼物。
杰瑞从地上开始。他穿过洞和通道去送礼物。
在送出所有礼物后,他又回到了地上。
在鼹鼠的世界里,有趣的是,奇数的鼹鼠是雄性,其他的是雌性。
当到达一个洞时,Jerry记下洞主人的性别(0代表女性,1代表男性)。
当他回到地面时,他将获得0-1的顺序。现在他想计算“和谐值”。
和声值表示给定“和声串”在上述序列中的出现次数。事件可能会重叠。
*/

标签:ch,笛卡尔,int,rson,st,节点
From: https://www.cnblogs.com/jinjiaqioi/p/18009615

相关文章

  • 数据过多时候,子查询改成left join减少笛卡尔积
    子查询SELECT cn.portal_idASportalId, count(id)ASnumFROM construction_package_wbs_nodecnWHERE cn.delete_flag=0 AND( cn.node_type='单位工程' ORcn.node_type='分部工程' ORcn.node_type='分项工程' ORcn.no......
  • 笛卡尔积
    项目中用到了数据组合问题,使用递归实现笛卡尔积,发现报内存溢出,给出解决办法:1functionDescartes1(list){2letresultList=[];3letsrcLength=list.length;4for(leti=1;i<srcLength;i++){5letpreList=i==1?list[i-1]:r......
  • 笛卡尔树入门
    笛卡尔树的定义笛卡尔树是一种二叉树,每一个结点由一个键值二元组\((k,w)\)构成。要求\(k\)满足二叉搜索树的性质,而\(w\)满足堆的性质。一个有趣的事实是,如果笛卡尔树的\(k,w\)键值确定,且\(k\)互不相同,\(w\)互不相同,那么这个笛卡尔树的结构是唯一的。——OIwiki笛......
  • 笛卡尔积、除、(外)连接等重要关系代数求解方法 概述
    关系代数这部分知识,在软考-数据库部分是比较重要的。   有五种基本的关系代数运算,并(符号为V)、差(符号为^)、投影()、笛卡尔积、选择,补充关系代数运算有,交、连接、除、广义投影、外连接。    1、笛卡尔积,从数学角度理解,就是将集合A和集合B中所有有序对元素集合。  ......
  • python基于动态数量个列表求笛卡尔积
    需求有N个list,分别是listA,listB,listC。。。等等,N的数量不确定,现在对这些list的所有可能组合的值求笛卡尔积,比如(listA,listB),(listA,listC),(listB,listC),(listA,listB,listC)。。。求这里每个组合的笛卡尔积。分析对实现以上需求,可分解为2个部分:1.求所有list的组合2.对所......
  • 第一章:笛卡尔坐标系
    第一章:笛卡尔坐标系1.一维数学在进入三维的学习之前,先厘清一些关于数字系统和计数的问题。自然数,又称计数数字。是几千年前发明的,可能是为了跟踪记录死羊(本书作者的神奇脑洞),也是数学的萌芽。将绵羊排成一排以便计数的习惯进而导致了数字排队的概念。负债概念的出现导致了负......
  • 数据库中什么是内连接、外连接、交叉连接、笛卡尔积;MySQL 的内连接、左连接、右连接有
    一、什么是内连接、外连接、交叉连接、笛卡尔积呢内连接(innerjoin):取得两张表中满足存在连接匹配关系的记录;外连接(outerjoin):不只取得两张表中满足存在连接匹配关系的记录,还包括某张表(或者两张表)中不满足匹配关系的记录。交叉连接(crossjoin):显示两张表所有记录一一对应,没有匹配关系......
  • 【学习笔记】(29) 笛卡尔树
    定义与性质笛卡尔树是一种二叉树,每一个结点由一个键值二元组\((k,w)\)构成。要求\(k\)满足二叉搜索树的性质,而\(w\)满足堆的性质。,也就是说,对于一个节点\(i\)的左儿子\(l_i\)和右儿子\(r_i\),一定满足\(l_i<i<r_i\)(下标\(k\)满足二叉搜索树的性质)且\(v_{l_i}\)与......
  • 并,交,差,笛卡尔积
        ......
  • 【学习笔记】笛卡尔树
    概述有若干二元组\((k,w)\),笛卡尔树要求关于\(k\)满足二叉搜索树的性质,关于\(w\)满足堆的性质。构建以要求\(w\)满足小根堆为例,使用单调栈维护当前的右链。现将所有二元组按\(k\)升序排序,每次插入一个元素时不断弹栈找到第一个小于\(w\)的节点,并将当前节点作为其右......