题意:给你两个数组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];
}
*/