https://www.jisuanke.com/contest/9342/challenges
题目描述
皮皮准备去旅游,共有n个景点可以选择,景点编号为1~n,每个景点都有一个“美观值”ai。共有m次查询,对于第i次查询,皮皮将从xi号景点开始游览,之后他会选择沿着编号递增的顺序选择游览其他景点,但是如果这个景点的美观值不大于他刚刚游览过的景点,他就会跳过这个景点。也就是说,皮皮在游览一个美观值为u的景点v后,他将游览的下一个景点是编号大于v、美观值大于u的,编号最小的景点。皮皮将一共访问yi个景点,请你输出他最后一个访问的景点编号,如果他不能访问yi个景点,输出-1。
(1 <= n,m <= 1e5)
Solution
O(nlogn)
倍增思想
令mov[i][j]为从i格子开始第2^j个比i大的位置。
处理mov[i][0]时也可用单调栈优化。
转移方程 : mov[j][i] = mov[mov[j][i - 1]][i - 1]
处理完mov数组后倍增跳格子即可。
代码
#include <bits/stdc++.h>
using namespace std;
const int SZ = 1e5 + 10;
int mov[SZ][17],a[SZ];
int n,m;
int main()
{
int T;
scanf("%d",&T);
while(T --)
{
memset(mov,0,sizeof(mov));
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i ++) scanf("%d",&a[i]);
for(int i = n - 1;i >= 1;i --)
{
int now = i + 1;
while(now != 0 && a[now] <= a[i]) now = mov[now][0];
mov[i][0] = now;
}
for(int i = 1;i < 17;i ++)
for(int j = 1;j <= n;j ++)
mov[j][i] = mov[mov[j][i - 1]][i - 1];
int x,y;
while(m --)
{
scanf("%d%d",&x,&y);
int now = x;
y --;
for(int i = 16;i >= 0;i --)
{
if(y & (1 << i))
now = mov[now][i];
}
if(now != 0) printf("%d\n",now);
else printf("-1\n");
}
}
return 0;
}
2020.6.9