小a的排列
链接:https://ac.nowcoder.com/acm/problem/22148
来源:牛客网
题目描述
小a有一个长度为n的排列。定义一段区间是"萌"的,当且仅当把区间中各个数排序后相邻元素的差为1
现在他想知道包含数x,y的长度最小的"萌"区间的左右端点
现在他想知道包含数x,y的长度最小的"萌"区间的左右端点
也就是说,我们需要找到长度最小的区间[l,r],满足区间[l,r]是"萌"的,且同时包含数x和数y
如果有多个合法的区间,输出左端点最靠左的方案。
输入描述:
第一行三个整数N,x,yN,x,y,分别表示序列长度,询问的两个数
第二行有nn个整数表示序列内的元素,保证输入为一个排列
输出描述:
输出两个整数,表示长度最小"萌"区间的左右端点
示例1
输入
5 2 3
5 2 1 3 4
输出
2 4
说明
区间[2,4]={2,1,3}包含了2,3且为“萌”区间,可以证明没有比这更优的方案
示例2
输入
8 3 5
6 7 1 8 5 2 4 3
输出
5 8
备注:
保证2⩽n⩽10^5,1⩽x,y⩽n
题解:
例如例子:8 2 5
8 2 3 1 5 6 7 4
应该输出 2 8
本题属于模拟题,静下心来就应该能做对。思路:先将每个数的位置存在pos数组中,然后找出x,y的位置,上面例子即2和5,作为最初位置,即至少输出的是2,5。然后从位置2~5中找出数值的最大值和最小值为1和5,所以包含2和5的区间至少要包含1~5这5个数。然后看看1~5这5个数中位置的最大值和最小值,即2和8,所以最小区间为2~8;然后我们需要对比最初区间和最小区间,将最初区间一点点扩展成最小区间,扩展的过程中我们需要每次都比较一下有没有更新最大值和最小值,如果更新,那么我们需要重新更新最小区间,知道最初区间和最小区间重合为止。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e5+10;
int n,prel,prer,x,y;
int a[maxn];
int pos[maxn];
int main()
{
scanf("%d%d%d",&n,&x,&y);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
pos[a[i]]=i;
}
//x,y最初的位置
prel=pos[x];
prer=pos[y];
int ml=prel;//x至少在的位置
int mr=prer;//y至少在的位置
int minn=inf,maxx=-1;
int L,R;
if(prel>prer)
swap(prel,prer);
for(int i=prel;i<=prer;i++)
{
minn=min(minn,a[i]);//x~y范围内数值最小的
maxx=max(maxx,a[i]);//x~y范围内数值最大的
}
//找到了minn,maxx,需要看看minn~maxx范围内数所对应的最小值最大值对应的具体位置
for(int i=minn;i<=maxx;i++)
{
ml=min(ml,pos[i]);
mr=max(mr,pos[i]);
}
while(prel>ml||prer<mr)
{
while(prel>ml)
{
minn=min(minn,a[--prel]);
maxx=max(maxx,a[prel]);
}
while(prer<mr)
{
minn=min(minn,a[++prer]);
maxx=max(maxx,a[prer]);
}
for(int i=minn;i<=maxx;i++)
{
ml=min(ml,pos[i]);
mr=max(mr,pos[i]);
}
}
printf("%d %d\n",prel,prer);
}
附官方题解:https://static.nowcoder.com/pdf/ncwc/牛客寒假训练营1题解.pdf