首页 > 其他分享 >Blocks(单调栈)

Blocks(单调栈)

时间:2024-02-22 11:56:09浏览次数:26  
标签:Blocks int sum 区间 端点 序列 单调

Blocks

题目描述

给出N个正整数a[1..N],再给出一个正整数k,现在可以进行如下操作:每次选择一个大于k的正整数a[i],将a[i]减去1,选择a[i-1]或a[i+1]中的一个加上1。经过一定次数的操作后,问最大能够选出多长的一个连续子序列,使得这个子序列的每个数都不小于

k。 总共给出M次询问,每次询问给出的k不同,你需要分别回答。

输入:第一行两个正整数N (N <= 1,000,000)和M (M <= 50)。 第二行N个正整数,第i个正整数表示a[i] (a[i] <= 10^9)。 第三行M个正整数,第i个正整数表示第i次询问的k (k <= 10^9)。

输出:共一行,输出M个正整数,第i个数表示第i次询问的答案。

样例

输入

5 6
1 2 1 1 5
1 2 3 4 5 6

输出

5 5 2 1 1 0

题解

只要区间平均值大于等于 \(k\) 就是合法的,可以让每一个值减 \(k\),这样只要区间和大于 \(0\) 就是合法的,所以可以维护前缀和 \(sum[i]=sum[i-1]+a[i]-k\) ,判断 \(sum[r]>=sum[l-1]\)。

建一个栈,先压一条从零开始的单调递减序列(不是单调栈,就是单纯递减序列),以栈首元素为左端点,如果一个点没有进栈,那它一定大于等于栈首元素,说明这是一个合法区间,直到有一个点比栈首小,这个区间不合法,进栈(如图1)。

然后得到一个单调递减的序列,以每个元素为左端点,到下一个元素之前的任意一点为右端点都是合法的,

但要求的是最大区间,为什么先要求单调递减序列呢?既然左端点确定了,那就倒序循环遍历右端点,单调递减序列就是为了从右往左找左端点,每遍历到一个点只需要按单调栈的方式,把栈首比它小的 \(pop\) ,记录最后一个就是最左端的,如果之后有一个点,比它小的已经被 \(pop\) 了怎么办?
这时它能找到的最优解一定小于之前的最优解,贪心直接无视它。

code
#include<bits/stdc++.h>
using namespace std;
const int N = 1000005;
#define int long long
int n,m,k,a[N],ans,sum[N],out,mx;

main()
{
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		mx=max(mx,a[i]);
	}
	while(m--)
	{
		deque <int> q;
		scanf("%lld",&k);
		out=0;
		if(k>mx)
		{
			printf("0 ");continue;
		} 
		for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i]-k;
		q.push_back(0);
		for(int i=1;i<=n;i++) if(sum[i]<sum[q.back()])
		q.push_back(i);
		int c=0;
		for(int i=n;i>=0;i--)
		{
			while(!q.empty()&&sum[i]>=sum[q.back()])
			{
				c=q.back();
				q.pop_back();
			}
			if(sum[c]<=sum[i])
			out=max(out,abs(i-c));
		}
		printf("%lld ",out);
	}
	return 0;	
} 

总结

想到平均值,与区间和有关,那就想办法维护前缀和,将区间和大于零转换为两个前缀和大小关系,建栈维护前缀和单调序列,找合法区间,再扩右边界(实际操作是遍历右边界,找已经维护好的左边界)。

标签:Blocks,int,sum,区间,端点,序列,单调
From: https://www.cnblogs.com/ppllxx/p/18026998

相关文章

  • Blocks(单调栈)
    题目链接因为可以操作无限次,所以相当于只要一个区间中的所有数相加的平均值大于k即成立首先,我们需要通过前缀和维护,可以在加的时候减去一个k,这样只用判断\(i>j且sum[i]-sum[j]>0\)则在[j+1,i]区间中存在满足题目的连续序列如图用红线指的是存入的<0并且单调递减的前缀和su......
  • blocks 单调栈、单调队列题解
    blocks题解:1、题面:2、分析:题意大概就是说,找一段最长的区间,并且这段区间的平均值>=k,那么我们可以对他的每一个值减去k,最终求和>=0即可。那我们需要对每个可能的左端点和右端点进行考虑,并以此让他们进行配对,看他们之间的区间和是否非负。那么我们先定住一个右端点,再依次考虑......
  • Blocks—单调栈
    思路:题目意思是求平均数>=k的最长序列先求出a[i]-k的前缀和就是求sum[i]-sum[j]>=0的最大i-j当j<=k<=isum[j]<=sum[k]j<=k<=isum[j]<=sum[k]更新i的时候,k就不如j优所以处理出来一个单调上升的数组(stak),那答案就在里面选倒序更新,单调栈一直减减就好因为如果用stak[top]更新了i,因......
  • [洛谷P3503][POI2010][BZOI2086]Blocks
    先看数据范围,n≤1e7,k≤1e9,暴力显然行不通,只能考虑单调栈;首先题目中说每一个数都要大于k,那么我们可以在初始化时就将每一个数都减去k,将问题转化为从正数中取出数加到负数里;然后维护一个前缀和,来判断一个区间是否符合要求;显然,当sum[j]-sum[i]≥0时,区间[i+1,j]符合题意,......
  • Blocks(单调栈)
    题干中说每次选择一个大于k的数,还要选他左右两个数其中之一加上一,最后问你最长的每个数不小于K的子序列。这些都是障眼法,其实就是问你最长的平均值大于或等于K的最长子序列,这样就明朗了。接下来就是找子序列的问题,因为要求的是区间最大平均值,所以不能再以单个数的值为依据来决......
  • [BZOJ1047][HAOI2007][AcWing1091]理想的正方形(单调队列)
    此题的数据相当大,暴力的显然过不了,即使是O(abn)的算法也会超时,所以只能考虑O(ablogn)或O(ab)的算法。50分暴力#include<bits/stdc++.h>usingnamespacestd;intn,a,b,m[1001][1001];intdx(intx,inty){ intmaxn=0,minn=0x7fffffff; for(inti=x;i<=x+n-1;++i){ for(in......
  • 单调队列、单调栈
    单调栈什么是单调栈单调栈是指一个栈内部的元素具有严格单调性的一种数据结构,分为单调递增栈和单调递减栈。单调栈的性质1.满足栈底到栈顶的元素具有严格单调性。2.满足栈的先进后出特性,越靠近栈顶的元素越先出栈元素进栈过程对于一个单调递减栈来说,若当前进栈的元素为a,如果a......
  • blocks——题解
    题目描述解析对于这道题,他要求大于k的数进行操作,所以直接让每个数减k,然后用前缀和维护一下与0比较就可以了,因为一段区间和的平均值大于k的话,那么这就是一个合法区间,即为操作后的这个区间和大于0,我们可以用一个单调递减栈去维护,先把比0小的推入栈中,因为要求最大区间,所以边界......
  • 学习笔记#5:单调队列优化&斜率优化
    学习笔记#5:单调队列优化&斜率优化单调队列首先要搞懂什么是单调队列。单调队列是一种求区间最值问题的一种方式,与其他RSQ问题的求解方法不同的是,它更善于解决滑动窗口式的RSQ问题,一般来说,假设我们要维护最大值,则需维护一个单调递减的队列,这样队首最大,每次取队首即可。而当......
  • 单调性优化
    【单调栈】柱状图最大矩形:首先我们有一个最简单的方法:\(O(n^3)\)枚举左右端点,然后再求中间的最小值,最后乘起来得到这段矩形组成的最大面积。求最小值,我们考虑到ST表,我们可以用它优化到\(O(n^2)\)。接下来我们考虑转换枚举思路:我们发现,一个矩形的大小,由它的左右端点,和它中间......