2019牛客多校第一场A. Equivalent Prefixes

  当然单调栈的解法这里就不提了。。。

   说一下笛卡尔树的解法

   其实这个题意一出来,只要学过笛卡尔树的人,都应该知道怎么解了。

   如果两个序列前K项,满足要求他们的子集合的最小值位置相同,那么他们数组的前K项构造出来的其实笛卡尔树其实是相同的。

   方法1.每次二分一个长度len,然后构造序列的前len项,然后判断这个数是否是相同的。判断规则就是每个节点的父亲节点以及儿子节点是否相同

  方法2.其实熟悉笛卡尔树性质的人,就知道笛卡尔树构造到位置i那么所有的节点1到i-1号节点的都在左边,

  画个图我们就知道,要求的最大长度,其实就是这两个笛卡尔树的前缀是否是相同的,也就是说,去掉len后面的节点后,这个树的形状是完全相同的

那么我们该如何更加优雅的判断呢???

  我们其实只需要考虑第i位的左儿子是否是相同的即可,如果是相同的,那么前i项的形状一定是相同的。因为对于一个节点,它的左儿子节点一定是其位置比他小的,而它的右儿子节点一定都是位置比他大的。证明我也不知道怎么证明。。。

方法1:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
int stk[maxn],tp;
int aa[maxn],bb[maxn];
void build(int n,int a[],int ch[][2],int fa[]){
   tp=0;
   for (int i=1;i<=n;i++)
   {
       int last=0;
       while(tp){
          if (a[stk[tp]]<=a[i]){
             if (ch[stk[tp]][1])ch[i][0]=ch[stk[tp]][1],fa[ch[stk[tp]][1]]=i;
             ch[stk[tp]][1]=i;fa[i]=stk[tp];
             break;
          }
          last=stk[tp--];
       }
       if (!tp && last)ch[i][0]=last,fa[last]=i;
       stk[++tp]=i;
   }
}
int fa[maxn],ch[maxn][2],faa[maxn],chh[maxn][2];
bool same(int len){
    for (int i=1;i<=len;i++){
        fa[i]=0;
        ch[i][0]=0;
        ch[i][1]=0;
        faa[i]=0;
        chh[i][0]=0;
        chh[i][1]=0;
    }
    build(len,aa,ch,fa);
    build(len,bb,chh,faa);
    int flag=0;
    for (int i=1;i<=len;i++){
        if (fa[i]!=faa[i])flag=1;
        if (ch[i][0]!=chh[i][0])flag=1;
        if (ch[i][1]!=chh[i][1])flag=1;
        if (flag)break;
    }
    if (flag)return 0;
    else return 1;
}
 
int main(){
 int n;
  while(~scanf("%d",&n)){
     memset(stk,0,sizeof(stk));
   //  memset(aa,0,sizeof(aa));
     for (int i=1;i<=n;i++){
        scanf("%d",&aa[i]);
     }
     for (int i=1;i<=n;i++){
        scanf("%d",&bb[i]);
     }
     int l=1;
     int r=n;
     int ans=1;
      while(l<=r){
         int mid=(l+r)>>1;
         if(same(mid)){
            ans=mid;
            l=mid+1;
         }else {
            r=mid-1;
         }
      }
      printf("%d\n",ans);
  }
  return 0;
}
View Code

方法2:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
int stk[maxn],tp;
int aa[maxn],bb[maxn];
void build(int n,int a[],int ch[][2],int fa[]){
   tp=0;//栈指针
   for (int i=1;i<=n;i++)
   {
       int last=0;//栈顶元素
       while(tp){
          if (a[stk[tp]]<=a[i]){//如果当前的值大于栈顶
            //如果现在栈顶元素的右儿子是存在的
            //需要把栈顶元素的右儿子断掉加入新节点的左儿子,
            //同时把新节点加入栈顶元素的右儿子
             if (ch[stk[tp]][1])ch[i][0]=ch[stk[tp]][1],fa[ch[stk[tp]][1]]=i;
             ch[stk[tp]][1]=i;fa[i]=stk[tp];
             break;
          }
          last=stk[tp--];//先除去栈顶元素
       }
       //如果栈为空,并且last有值的话,把这个值加入左儿子
       if (!tp && last)ch[i][0]=last,fa[last]=i;
       stk[++tp]=i;
   }
}
int fa[maxn],ch[maxn][2],faa[maxn],chh[maxn][2];
int main(){
 int n;
  while(~scanf("%d",&n)){
     memset(stk,0,sizeof(stk));
   //  memset(aa,0,sizeof(aa));
     for (int i=1;i<=n;i++){
        fa[i]=0;
        faa[i]=0;
        ch[i][0]=0;
        ch[i][1]=0;
        chh[i][0]=0;
        chh[i][1]=0;
     }
     for (int i=1;i<=n;i++){
        scanf("%d",&aa[i]);
     }
     for (int i=1;i<=n;i++){
        scanf("%d",&bb[i]);
     }
     int ans=1;
     build(n,aa,ch,fa);
     build(n,bb,chh,faa);
     int cnt=1;
     for (int i=2;i<=n;i++){
        if (ch[i][0]==chh[i][0])
          cnt++;
        else
          break;
     }
     printf("%d\n",cnt);
  }
  return 0;
}
View Code

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值