牛客练习赛25 B 最长区间

博客围绕求长度为n的序列a中最长连续严格上升区间长度展开,同时涉及m次修改,每次修改后输出结果。采用线段树维护最长区间,运用分治思想,考虑三种情况求解,还给出了节点递推式,并附上AC代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

链接:https://www.nowcoder.com/acm/contest/158/B
来源:牛客网
 

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

给你一个长度为 n 的序列 a ,求最长的连续的严格上升区间的长度。
同时会进行 m 次修改,给定 x , y ,表示将 ax 修改为 y ,每次修改之后都要求输出答案。

输入描述:

第一行 2 个数 n,m,表示序列长度,修改次数; 
接下来一行 n 个数表示  ;
接下来 m 行,每行 2 个数 x , y ,描述一次修改。

输出描述:

第一行 1 个数表示最初的答案;
接下来 m 行,第 i 行 1 个数表示第 i 次修改后的答案。

示例1

输入

复制

4 3
1 2 3 4
3 1
2 5
3 7

输出

复制

4
2
2
3

说明

序列变换如下:
1  2  3  4
1  2  1  4
1  5  1  4
1  5  7  4

备注:

n,m ≤ 100000,1 ≤ x ≤ n,1 ≤ ai,y ≤ 100

大致思路:

                 用线段树来维护最长区间。其实用的是分治思想。我们来考虑这样一个情况。如何寻找区间[l,r]的最长连续区间?

其实很简单,只有三种可能:

1、完全位于[l,mid]区间中;

2、完全位于[mid+1,r]区间内;

3、跨立这个两个区间(其中a[mid]一定小于a[mid+1]才有可能)

我们采用分治的思想,很容易求出前两种情况的最优解。对于第三种情况,一定是[l,mid]包含mid的最长区间加上[mid+1,r]包含mid+1的最长区间。

为了使问题层次更加清晰,我们每个节点用Llen表示包含l的最长区间,Rlen表示包含r的最长区间,maxlen表示最长区间。

那么,我们可以建立如下递推式:

                        

tree[k].maxlen  =  max ( tree[k<<1].maxlen  ,  tree[k<<1|1].maxlen  ,  tree[k<<1].Rlen + tree[k<<1|1].Llen );

 

其中不止是maxlen需要更新,Rlen和Llen也需要更新;

                         tree[k<<1].Llen    其中a[mid]>=a[mid+1]

tree[k].Llen=    

                        tree[k<<1].Llen+tree[k<<1|1].Llen 其中a[mid]<a[mid+1]且tree[k<<1].Llen对于该节点的区间长度

 

                         tree[k<<1|1].Rlen    其中a[mid]>=a[mid+1]

tree[k].Rlen=    

                        tree[k<<1|1].Rlen+tree[k<<1].Rlen 其中a[mid]<a[mid+1]且tree[k<<1].Rlen对于该节点的区间长度

 

附上ac代码

 

                       

# include <iostream>
# include <cstring>
# include <cstdio>
# define L(x) (x<<1)
# define R(x) (x<<1|1)

using namespace std;

const int MAXN = 1e5+10;

struct node
{
	int l,r,maxlen;
	int Llen,Rlen;
	
	int mid(){
		return (l+r)>>1;
	}
	
	int len(){
		return r-l+1;
	}
};

node tree[MAXN<<2];
int a[MAXN];

void pushup(int k)
{
	tree[k].Llen=tree[L(k)].Llen;

	tree[k].Rlen=tree[R(k)].Rlen;

	tree[k].maxlen=max(tree[L(k)].maxlen,tree[R(k)].maxlen);

	int mid = tree[k].mid();

	if(a[mid]<a[mid+1])
	{
		if(tree[L(k)].Llen==tree[L(k)].len())
			tree[k].Llen+=tree[R(k)].Llen;

		if(tree[R(k)].Rlen==tree[R(k)].len())
			tree[k].Rlen+=tree[L(k)].Rlen;

		tree[k].maxlen=max(tree[k].maxlen,tree[L(k)].Rlen+tree[R(k)].Llen);
	}
	return ;
}

void build(int l,int r,int k)
{
	tree[k].l=l;
	tree[k].r=r;
	if(l==r)
	{
		tree[k].maxlen=tree[k].Llen=tree[k].Rlen=1;
		return ;
	}
	int mid = tree[k].mid();
	build(l,mid,L(k));
	build(mid+1,r,R(k));
	pushup(k);
	return ; 
}

void updata(int pos, int k)
{
	if(tree[k].len()==1)
		return ;
	int mid=tree[k].mid();
	if(pos<=mid)
		updata(pos,L(k));
	else
		updata(pos,R(k));
	pushup(k);
	return ;
}
int main()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	
	memset(tree,0,sizeof tree);	
	build(1,n,1);
	printf("%d\n",tree[1].maxlen);
	while(m--)
	{
		int pos,v;
		scanf("%d%d",&pos,&v);
		a[pos]=v;
		updata(pos,1);
		printf("%d\n",tree[1].maxlen);
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值