ICPC NEAU Programming Contest 2020 D 旅游 【单调栈+倍增】

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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值