2019牛客暑期多校训练营 A 笛卡尔树 OR 单调栈
登录—专业IT笔试面试备考平台_牛客网
https://ac.nowcoder.com/acm/contest/881/A
题意:给你两个数组a,b,大小为n,让你寻找一个数p (1<= p <= n) ,使之在 1~p 任意一个区间中a,b数组的最小值下标相同。
笛卡尔树:
概念
笛卡尔树的树根是这一子树中键值最小(或最大)的元素;且对于某个序列的笛卡尔树,其任一子树的中序遍历恰好是对应了原序列中的一段连续区间。
性质
我们会发现笛卡尔树同时满足二叉树搜索和堆的性质:
- 中序遍历即为原数组序列
- 父节点的键值均小于(或大于)其左右子节点的键值
我们可以将问题转化为 1~p内,a和b的笛卡尔树是否相同
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n;
int a[N];
int b[N];
int l[2][N];//l[i] 表示 i 节点的左孩子的数组下标
int r[2][N];//r[i] 表示 i 节点的右孩子的数组下标
int rt[2];
stack<int> s;
int dfs(int u){
if(l[0][u]!=l[1][u] || r[0][u]!=r[1][u])
return 0;
int ok = 1;
if(l[0][u])
ok &= dfs(l[0][u]);
if(r[0][u])
ok &= dfs(r[0][u]);
return ok;
}
int gao(int x){
for(int i = 1; i <= x; i++){
l[0][i] = r[0][i] = 0;
while(!s.empty() && a[i] < a[s.top()])
l[0][i] = s.top() , s.pop();
if(!s.empty()){//栈顶元素 < a[i] ,i为其右孩子
r[0][s.top()] = i;
}
s.push(i);
}
while(!s.empty()){
rt[0] = s.top(),s.pop();
}
for(int i = 1; i <= x; i++){
l[1][i] = r[1][i] = 0;
while(!s.empty() && b[i] < b[s.top()])
l[1][i] = s.top() , s.pop();
if(!s.empty()){
r[1][s.top()] = i;
}
s.push(i);
}
while(!s.empty()){
rt[1] = s.top(),s.pop();
}
if(rt[0]!=rt[1])
return 0;
return dfs(rt[0]);
}
int main(){
while(~scanf("%d",&n)){
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++){
scanf("%d",&b[i]);
}
int l=1,r=n,mid;
while(l<=r){
mid=(l+r)/2;
if(gao(mid)) l = mid + 1;
else r = mid - 1;
}
cout << r << "\n";
}
return 0;
} 单调栈:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n;
int a[N];
int b[N];
int l[2][N];
int r[2][N];
// l[i] r[i] 表示以 i 为最小值的最大区间左右边界 区间长度:r[i] - l[i] -1
//在这个题里面只需要l[i]
stack<int> s;
void init(int *c,int k){
l[k][0] = 0;
for(int i = 1; i <= n; i++){
while(!s.empty() && c[i] < c[s.top()]){
r[k][s.top()] = i; s.pop();
}
if(!s.empty()){
l[k][i] = s.top();
}
else l[k][i] = 0;
s.push(i);
}
while(!s.empty()){
r[k][s.top()] = n+1;
s.pop();
}
}
int gao(int x){
for(int i = 1; i <= x; i++){
if(l[0][i]!=l[1][i]){
return 0;
}
}
return 1;
}
int main(){
while(~scanf("%d",&n)){
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++){
scanf("%d",&b[i]);
}
init(a,0);
init(b,1);
int l=1,r=n,mid;
while(l<=r){
mid=(l+r)/2;
if(gao(mid)) l = mid + 1;
else r = mid - 1;
}
cout << r << "\n";
}
return 0;
} 