Description
皮皮准备去旅游,共有n个景点可以选择,景点编号为1~n,每个景点都有一个“美观值”ai。共有m次查询,对于第i次查询,皮皮将从xi号景点开始游览,之后他会选择沿着编号递增的顺序选择游览其他景点,但是如果这个景点的美观值不大于他刚刚游览过的景点,他就会跳过这个景点。也就是说,皮皮在游览一个美观值为u的景点v后,他将游览的下一个景点是编号大于v、美观值大于u的,编号最小的景点。皮皮将一共访问yi个景点,请你输出他最后一个访问的景点编号,如果他不能访问yi个景点,输出
-1
Input
输入第一行一个整数T,代表接下来有T组测试数据
接下来T组,每组第一行有两个整数n,m,第二行有n个整数ai,接下来m行,每行有两个整数xi,yi
Output
对于每组测试数据,输出一行,一个整数。如果能游览yi个景点,输出最后访问的景点编号,否则输出
-1
数据范围
1≤T≤5
1≤n,m≤10^5
1≤ai≤10^9
1≤xi,yi≤n
样例输入
1
15 10
1 2 3 4 5 3 4 5 6 7 5 6 7 8 9
1 1
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
1 10
样例输出
1
2
3
4
5
9
10
14
15
-1
题目大意:
在一个序列中重复 y 次去往比 a[x]大的第一个位置。
分析:
首先,预处理出每个位置的下一跳位置,即比 a[i]大的第一个位置 b[i] 。
那么这个问题就转化为,在序列中,对于每个位置 i ,求 j>i 并且 a[j]>a[i] 的最小的 j ,即 b[i] = j ,这可以用单调栈求出,需要注意的是,这里需要从后往前求。
然后考虑每次询问,如果暴力按序跳转,那么复杂度太大,不可行,所以考虑倍增。
定义 f[i][k] 表示从位置 i 往后 2^k 步到达的位置,则 f[i][0] = b[i],f[i][k] = f[f[i][k-1]][k-1]。
查询时,只需要将 y 分成 2 的幂即可。
具体解释见代码。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <queue>
#define INF 0x3f3f3f3f
#define mst(a,num) memset(a,num,sizeof a)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define repd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef vector<int> VI;
const ll mod = 1e9 + 7;
const int maxn = 100000 + 5;
ll a[maxn];
int len[maxn];
VI sta;
int f[maxn][35];
int main() {
int t;
scanf("%d",&t);
while(t--){
int n,m;
scanf("%d%d",&n,&m);
rep(i,1,n) scanf("%lld",&a[i]),len[i]=0,f[i][0]=0;
sta.clear();
sta.push_back(0);
a[0]=1e18; //在栈底设置一个无穷大
repd(i,n,1){
while(!sta.empty()&&a[sta.back()]<=a[i]){ //单调栈
sta.pop_back();
}
f[i][0]=sta.back();
sta.push_back(i);
}
len[0]=0;
repd(i,n,1) len[i]=len[f[i][0]]+1; //求取每个位置的上升子序列长度
rep(j,1,30){ //预处理出f[i][k]
f[0][j-1]=0;
rep(i,1,n){
f[i][j]=f[f[i][j-1]][j-1];
}
}
int x,y;
rep(q,1,m){
scanf("%d%d",&x,&y);
if(len[x]<y){
printf("-1\n");
}
else{
int p=0;
y--;
while(y){ //将y划分为2的幂次
if(y&1){
x=f[x][p];
}
p++;
y>>=1;
}
printf("%d\n",x);
}
}
}
return 0;
}