2019牛客暑期多校训练营(第一场) A Equivalent Prefixes

题意:给你两个数组a,b,大小为n,让你寻找一个数p (1<= p <= n) ,使之在 1~p 任意一个区间中a,b数组的最小值下标相同。
分析:

方法一:单调栈维护两个数组内元素单调递增,当栈内元素 的个数不相同时,退出循环(本质是方法二)。

方法二:找每个点的左边的比它小的数的所在的位置,然后如果两点的左边比它小的数的位置不同,那么肯定这一位就不能取了。

方法三:二分+st表,在二分p的过程中,我们这样来判断结尾位置mid是否合法:询问1~mid 区间两个数组中的最小值下标是否一致,如果不一致直接返回false,否则 以最小值下标minid为分界点递归处理1~minid,minid+1~mid。

方法四:笛卡尔树。

代码一(方法一):

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+9;
 
int a[N],b[N];
int u[N],v[N];
 
int main(){
    int n;
    while(cin>>n){
        for(int i=1;i<=n;i++){
            scanf("%d",a+i);
            u[i]=i-1;
            while(a[u[i]]>a[i])u[i]=u[u[i]];
        }
        for(int i=1;i<=n;i++){
            scanf("%d",b+i);
            v[i]=i-1;
            while(b[v[i]]>b[i])v[i]=v[v[i]];
        }
        v[n+1]=u[n+1]+1;
        for(int i=1;i<n+2;i++)if(v[i]!=u[i]){
            cout<<i-1<<'\n';
            break;
        }
    }
    return 0;
}

代码二(方法二):

#include <cstdio>
#include <queue>
#include <cstring>
#include <stack>
#include <iostream>
#include <cmath>
using namespace std;
stack<int> sta1,sta2;
int main(){
    int n;
    while(cin >> n){
        int a[100005],b[100005];
        for(int i = 1; i <= n; i++) cin >> a[i];
        for(int i = 1; i <= n; i++) cin >> b[i];
        int k = 1;
        while(!sta1.empty()) sta1.pop();
        while(!sta2.empty()) sta2.pop();
        sta1.push(a[1]);
        sta2.push(b[1]);
        while(k < n){
            while(!sta1.empty() && sta1.top() > a[k+1])
                sta1.pop();
            if(sta1.empty() || a[k+1]>sta1.top()) sta1.push(a[k+1]);
            while(!sta2.empty() && sta2.top() > b[k+1]) sta2.pop();
            if(sta2.empty() || b[k+1] > sta2.top()) sta2.push(b[k+1]);
            if(sta1.size() != sta2.size()) break;
            k++;
        }
        cout << k << endl;
    }
    return 0;
}

代码三(方法三):

#include<bits/stdc++.h>
const int N = 1e5+5;
using namespace std;
int n,a[N],b[N],dp[N][20],dp2[N][20];

void init() {
    for(int i=0;i<n;i++) dp[i][0]=dp2[i][0]=i;
    for(int j=1;(1<<j)-1<n;j++)
        for(int i=0;i+(1<<j)-1<n;i++)
            if(a[dp[i][j-1]]<=a[dp[i+(1<<j-1)][j-1]]) dp[i][j]=dp[i][j-1];
            else dp[i][j]=dp[i+(1<<j-1)][j-1];
    for(int j=1;(1<<j)-1<n;j++)
        for(int i=0;i+(1<<j)-1<n;i++)
            if(b[dp2[i][j-1]]<=b[dp2[i+(1<<j-1)][j-1]]) dp2[i][j]=dp2[i][j-1];
            else dp2[i][j]=dp2[i+(1<<j-1)][j-1];
}

int idxa(int s,int v){
    int k=(int)(log(v-s+1.0)/log(2.0));
    if (a[dp[s][k]]<=a[dp[v-(1<<k)+1][k]]) return dp[s][k];
    else return dp[v-(1<<k)+1][k];
}

int idxb(int s,int v){
    int k=(int)(log(v-s+1.0)/log(2.0));
    if (b[dp2[s][k]]<=b[dp2[v-(1<<k)+1][k]]) return dp2[s][k];
    else return dp2[v-(1<<k)+1][k];
}

int ck(int l,int r) {
    if(l>=r) return 1;
    int x=idxa(l,r),y=idxb(l,r);
    if(x!=y) return 0;
    else return ck(l,x-1)&&(ck(x+1,r));
}

void sv() {
    int l=0,r=n-1,ans=0;
    while(l<=r) {
        int m=(l+r)/2;
        if(ck(0,m)) l=m+1,ans=m;
        else r=m-1;
    }
    printf("%d\n",ans+1);
}

int main(){
    while(scanf("%d",&n)!=EOF){
        for(int i=0;i<n;i++) scanf("%d",&a[i]);
        for(int i=0;i<n;i++) scanf("%d",&b[i]);
        init();
        sv();
    }
    return 0;
}

/*
void makeRMQ(int n,int b[]){//在i到1<<j区间内的最小值的下标
    for(int i=0;i<n;i++)
        dp[i][0]=i;//区间长度等于0时
    for(int j=1;(1<<j)-1<n;j++)
        for(int i=0;i+(1<<j)-1<n;i++)
            if(b[dp[i][j-1]]<=b[dp[i+(1<<j-1)][j-1]])
            //这里一定要加等号,就是相等的时候取下标小的
                dp[i][j]=dp[i][j-1];
            else
                dp[i][j]=dp[i+(1<<j-1)][j-1];
}
int rmqIndex(int s,int v,int b[]){//在区间s->v之间找区间最小值,
                                    //加等号,取小标小的
    //1<<k+1的长度必须覆盖[s,v]
    int k=(int)(log(v-s+1.0)/log(2.0));//k就是那个区间[s,v]长度的log2
    if (b[dp[s][k]]<=b[dp[v-(1<<k)+1][k]])
        return dp[s][k];
    else
        return dp[v-(1<<k)+1][k];
}

*/

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值