牛客练习赛85-哲学家的沉思 #倍增##单调栈#

牛客练习赛85-哲学家的沉思 #倍增##单调栈#

题目大意

给定(n)个数字,第(i)个为(a_i), 有(q)个询问,对于每个询问,给定(l,r),将序列([l,r])划分为最少的子序列,使得每个子序列的第一个数在该子序列内最大

复杂度要求:(O(qlog 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
" , ans + 1);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/dream1024/p/14960240.html