笛卡尔树
笛卡尔树基本概念
笛卡尔树是基于一个静态序列
笛卡尔树有三点要求需要满足:
-
笛卡尔树是二叉树。
-
笛卡尔树的编号的中序遍历为
,权值中序遍历为 。 -
笛卡尔树的权值满足大根堆或者小根堆的性质。
这里可以看一个图,节点上标的是权值。
为了更加方便,笛卡尔树的节点编号与序列
笛卡尔树的构造
这里我们先给出笛卡尔树的性质,这里以小根笛卡尔树为例:
-
对于树上一条深度单调递增的路径,其权值单调不减。
-
,以 为根的子树恰好涵盖 序列中 区间的所有位置。 -
,其在笛卡尔树上的最近公共祖先的权值 恰好为 序列中 区间的最小值。
于是我们可以在线性时空内构造笛卡尔树。
我们考虑维护笛卡尔树上最右端的链,则有这条链的编号和权值都不减,于是我们可以用单调栈维护。
我们从前向后遍历
-
,直接将 入栈。事实上就是在笛卡尔树的最右端的最下面插入这个元素。 -
否则,开始弹出栈,直到栈为空或者栈顶元素权值不小于
,这里设最后一个弹出的元素的下标设为 。则把 入栈, 变为 的左儿子。
这里再挂个图,方便理解:
然后我们就建完树了,可以开始看题了。
笛卡尔树
考虑直接按照上面的步骤建树,维护一下左右儿子即可,没有需要特别注意的地方,所以直接放一下代码:
#include<bits/stdc++.h>
#define int long long
#define pii pair<int,int>
#define x first
#define y second
#define N 10000005
using namespace std;
int n,a[N],stk[N],rt,res1,res2;
pii w[N];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
int top=0,cur=0;
for(int i=1;i<=n;i++){
cur=top;
while(top&&a[stk[cur]]>a[i])cur--;//弹栈
if(cur)w[stk[cur]].y=i;//没弹完代表有东西更小,作为右儿子
if(cur<top)w[i].x=stk[cur+1];//有东西出栈了,把出栈的最后一个节点作为当前点左儿子
stk[++cur]=i;//入栈
top=cur;
}
for(int i=1;i<=n;i++){
res1^=(i*(w[i].x+1));
res2^=(i*(w[i].y+1));
}
cout<<res1<<' '<<res2;
return 0;
}
树的序
类似于板子,说了这么多,其实就是对笛卡尔树求一个前序遍历,所以直接上代码:
#include<bits/stdc++.h>
#define int long long
#define pii pair<int,int>
#define x first
#define y second
#define N 10000005
using namespace std;
int n,a[N],stk[N],rt,res1,res2;
pii w[N];
void dfs(int u){
if(u==0)return;
cout<<u<<' ';
dfs(w[u].x);
dfs(w[u].y);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
int x;
cin>>x;
a[x]=i;
}
int top=0,cur=0;
for(int i=1;i<=n;i++){
cur=top;
while(top&&a[stk[cur]]>a[i])cur--;
if(cur)w[stk[cur]].y=i;
if(cur<top)w[i].x=stk[cur+1];
stk[++cur]=i;
top=cur;
}
dfs(stk[1]);
return 0;
}
Yet Another Array Counting Problem
笛卡尔树与
于是若两个序列的所有最左端最大值全部相同,即两棵树的形态一模一样,,也就是说,我们要统计的是每个点权值不超过
于是我们可以使用
我们设
显然有
这里解释一下为什么一个减一,一个不减。因为左子树的权值要严格小于当前点权值,右子树只需不大于即可。
还有一个小错误,我们必须使用前进行
于是我们就写完了,然后放一下代码:
#include<bits/stdc++.h>
#define int long long
#define pii pair<int,int>
#define x first
#define y second
#define N 1000005
#define mod 1000000007
using namespace std;
int T,n,m,a[N],stk[N];
pii w[N];
vector<vector<int>>f;
void dfs(int u){
for(int i=1;i<=m;i++){
f[u][i]=1;
}
if(w[u].x)dfs(w[u].x);
if(w[u].y)dfs(w[u].y);
if(w[u].x){
for(int i=1;i<=m;i++){
(f[u][i]*=f[w[u].x][i-1])%=mod;
}
}
if(w[u].y){
for(int i=1;i<=m;i++){
(f[u][i]*=f[w[u].y][i])%=mod;
}
}
for(int i=2;i<=m;i++){
(f[u][i]+=f[u][i-1])%=mod;
}
}
signed main(){
cin>>T;
while(T--){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
int top=0,cur=0;
for(int i=1;i<=n;i++){
cur=top;
while(cur&&a[stk[cur]]<a[i])cur--;
if(cur)w[stk[cur]].y=i;
if(cur<top)w[i].x=stk[cur+1];
stk[++cur]=i;
top=cur;
}
f.resize(n+1);
for(int i=1;i<=n;i++){
f[i].resize(m+1);
}
dfs(stk[1]);
cout<<f[stk[1]][m]<<'\n';
for(int i=1;i<=top;i++){
stk[i]=0;
}
for(int i=1;i<=n;i++){
w[i]={0,0};
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探