牛客练习赛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;
}
posted @ 2021-07-01 18:48  追梦人1024  阅读(59)  评论(0编辑  收藏  举报