笛卡尔树
Part 1:知识点
笛卡尔树是一种二叉树,每个节点有两个两个值
其中
一些性质
-
任何子节点的
小于(或大于)父节点的 -
对于任何父节点,左节点的
小于父节点的 ,右节点的 大于父节点的 -
若
值为数组下标,那么任意两个节点 的最近公共祖先就是区间 的极值
笛卡尔树的构建
我们以
每次插入一个节点
可以证明,这样插入是满足笛卡尔树的性质的
由于每个节点只会进出右链一次,所以时间复杂度
Part 2:习题
P5854 【模板】笛卡尔树
建立一个单调栈去维护右链,这个栈单调递增
插入一个节点
最后将
不断重复即可,最后以 sta[1]
作为根节点
#include<bits/stdc++.h>
using namespace std;
const int N=10000010;
int n,a[N];
int sta[N],t,ch[N][2];
long long L,R;
int read()
{
int x=0,f=1;
char c=getchar();
while(c<'0' || c>'9')
{
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0' && c<='9')
{
x=(x<<1)+(x<<3)+(c^'0');
c=getchar();
}
return x*f;
}
int main()
{
n=read();
for(int i=1; i<=n; i++)
a[i]=read();
sta[++t]=0;
for(int i=1; i<=n; i++)
{
while(t && a[sta[t]]>a[i])
ch[i][0]=sta[t--];
ch[sta[t]][1]=i;
sta[++t]=i;
}
for(int i=1; i<=n; i++)
{
L^=1LL*i*(ch[i][0]+1);
R^=1LL*i*(ch[i][1]+1);
}
printf("%lld %lld",L,R);
return 0;
}
P1377 [TJOI2011] 树的序
满足二叉搜索树性质的是键值
以此构建一棵笛卡尔树后输出中序遍历即可
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n,a[N];
int sta[N],t,ch[N][2];
void dfs(int x)
{
if(x)
printf("%d ",x);
if(ch[x][0])
dfs(ch[x][0]);
if(ch[x][1])
dfs(ch[x][1]);
}
int main()
{
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
int x=0;
scanf("%d",&x);
a[x]=i;
}
sta[++t]=0;
for(int i=1; i<=n; i++)
{
while(t && a[sta[t]]>a[i])
ch[i][0]=sta[t--];
ch[sta[t]][1]=i;
sta[++t]=i;
}
dfs(sta[1]);
return 0;
}
P3793 由乃救爷爷
不带修的
利用笛卡尔树的性质三,我们可以将问题转化成求
但由于这题数据随机,所以我们直接从根节点暴力向下跳即可
#include<bits/stdc++.h>
using namespace std;
const int N=20000010;
int n,m,s,a[N];
int sta[N],t,ch[N][2];
unsigned long long ans;
namespace GenHelper
{
unsigned z1,z2,z3,z4,b;
unsigned rand_()
{
b=((z1<<6)^z1)>>13;
z1=((z1&4294967294U)<<18)^b;
b=((z2<<2)^z2)>>27;
z2=((z2&4294967288U)<<2)^b;
b=((z3<<13)^z3)>>21;
z3=((z3&4294967280U)<<7)^b;
b=((z4<<3)^z4)>>12;
z4=((z4&4294967168U)<<13)^b;
return (z1^z2^z3^z4);
}
}
void srand(unsigned x)
{
using namespace GenHelper;
z1=x;
z2=(~x)^0x233333333U;
z3=x^0x1234598766U;
z4=(~x)+51;
}
int read()
{
using namespace GenHelper;
int a=rand_()&32767;
int b=rand_()&32767;
return a*32768+b;
}
int query(int l,int r)
{
int cur=sta[1];
while(1)
{
if(l<=cur && cur<=r)
return a[cur];
if(cur<l)
cur=ch[cur][1];
else
cur=ch[cur][0];
}
}
int main()
{
scanf("%d%d%d",&n,&m,&s);
srand(s);
for(int i=1; i<=n; i++)
a[i]=read();
sta[++t]=0;
for(int i=1; i<=n; i++)
{
while(t && a[sta[t]]<a[i])
ch[i][0]=sta[t--];
ch[sta[t]][1]=i;
sta[++t]=i;
}
for(int i=1; i<=m; i++)
{
int l=read()%n+1;
int r=read()%n+1;
if(l>r)
swap(l,r);
ans+=(unsigned long long)query(l,r);
}
printf("%llu",ans);
return 0;
}
P6453 [COCI2008-2009#4] PERIODNI
(双倍经验)
一道笛卡尔树的经典题目
以
这样做之后可以将原表格横向切割分成若干个矩形,当前矩形的长为
设
后面的那坨组合数与阶乘相乘表示在当前节点表示的矩形当中放
注意到后面的式子只和
后面那坨式子可以
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=510,M=1000010;
const LL MOD=1e9+7;
int n,k,a[N],ch[N][2],sta[N],t,size[N];
LL fac[M],inv[M],f[N][N],g[N][N];
LL ksm(int x,int y)
{
LL res=1;
while(y)
{
if(y&1)
res=(1LL*res*x)%MOD;
x=1LL*x*x%MOD;
y>>=1;
}
return res;
}
void prework()
{
fac[0]=inv[0]=1;
for(int i=1; i<=M-10; i++)
{
fac[i]=(fac[i-1]*i)%MOD;
inv[i]=ksm(fac[i],MOD-2);
}
}
LL C(int x,int y)
{
if(y>x)
return 0;
return fac[x]*inv[x-y]%MOD*inv[y]%MOD;
}
void dfs(int x,int fa)
{
if(ch[x][0])
dfs(ch[x][0],x);
if(ch[x][1])
dfs(ch[x][1],x);
size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
int h=a[x]-a[fa];
LL tmp=0;
for(int i=0; i<=size[ch[x][0]]; i++)
for(int j=0; j<=size[ch[x][1]]; j++)
(g[x][i+j]+=f[ch[x][0]][i]*f[ch[x][1]][j]%MOD)%=MOD;
for(int i=0; i<=size[x]; i++)
for(int j=0; j<=size[x]-1; j++)
(f[x][i]+=g[x][j]*C(size[x]-j,i-j)%MOD*C(h,i-j)%MOD*fac[i-j]%MOD)%=MOD;
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
sta[++t]=0;
for(int i=1; i<=n; i++)
{
while(t && a[sta[t]]>a[i])
ch[i][0]=sta[t--];
ch[sta[t]][1]=i;
sta[++t]=i;
}
prework();
f[0][0]=1;
dfs(sta[0],0);
printf("%lld",f[sta[1]][k]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?