牛客练习赛85-哲学家的沉思 #倍增##单调栈#
牛客练习赛85-哲学家的沉思 #倍增##单调栈#
题目大意
给定\(n\)个数字,第\(i\)个为\(a_i\), 有\(q\)个询问,对于每个询问,给定\(l,r\),将序列\([l,r]\)划分为最少的子序列,使得每个子序列的第一个数在该子序列内最大
复杂度要求:\(O(q\log n)\)
读入:
n q
a1 a2 ... a_n
l1 r1
l2 r2
...
l_q r_q
样例一
输入
5 2
2 5 4 3 6
3 5
1 2
输出
2
2
说明
对于询问1,可以把[3,5]划分为[3,4],[5,5]。对于询问2,可以把[1,2]划分为[1,1],[2,2]。
样例二
输入
3 2
2 2 3
1 2
1 3
输出
1
2
说明
对于询问1,可以把[1,2]划分为[1,2]。对于询问2,可以把[1,3]划分为[1,2],[3,3]。
思路
不难的一道题,容易发现,题目要求的是以\(l\)开头的,在\([l,r]\)范围内的严格上升(可不连续)子序列的长度用样例一的第一个询问举例,[3,5]
包含序列4,3,6
,以4
开头的严格上升子序列为:4,6
,所以输出2
不难想到倍增,\(f_{i,j}\)表示第\(i\)位后面比\(a_i\)大的第\(2^j\)个数的下标,用类似倍增LCA的方式求解即可
代码
#include <iostream>
#include <cstdio>
using namespace std;
int read(){
int re = 0 , sig = 1;
char c = getchar();
while(c < '0' || c > '9'){
if(c == '-')sig = -1;
c = getchar();
}
while(c >= '0' && c <= '9'){
re = (re << 1) + (re << 3) + c - '0';
c = getchar();
}
return re * sig;
}
#define N 100010
int n , q;
int Stack[N] , top = 0;
int *stack = Stack + 1;
int a[N];
int f[N][25];
int main() {
n = read() , q = read();
for(int i = 1 ; i <= n ; i++)
a[i] = read();
for(int i = 1 ; i <= n ; i++) {//找比a[i]大的第一个a[j] (j>i)即f[i][0], 单调栈解决
while(a[stack[top - 1]] < a[i] && top > 0) {
f[stack[top - 1]][0] = i;
--top;
}
stack[top] = i;
++top;
}
while(top > 0) {
f[stack[top - 1]][0] = n + 1;
--top;
}
for(int i = 0 ; i <= 20 ; i++)//求f
f[n + 1][i] = n + 1;
for(int i = 1 ; i <= 20 ; i++)
for(int j = n ; j > 0 ; j--)
f[j][i] = f[ f[j][i - 1] ][i - 1];
while(q--) {
int l = read() , r = read();
int ans = 0;
for(int i = 20 ; i >= 0 ; i--) {
if(f[l][i] <= r) {
l = f[l][i];
ans += (1 << i);
}
}
printf("%d\n" , ans + 1);
}
return 0;
}