CodeForces - 935F Fafa and Array [想法+线段树]

本文介绍了一道关于区间操作的问题,通过线段树优化实现高效查询与更新。讨论了如何利用线段树记录差值及对答案的贡献,以解决特定类型的区间操作问题。

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

题意:给你n个数,定义f(a)为


有两种操作:

①在区间[L,R]选一个数加上x,使得f(a)最大,该操作不影响原数组。

②向一段区间[L,R]全部加上x。

题解:首先我们先得出所有的差值,对于每一次询问①,假设在i这个位置上的数加上x,那么会影响到cha[i-1]与cha[i],然后我们考虑cha[i-1]与cha[i]对答案的影响,可以分为两种:

①cha[i-1]<=0或者cha[i]>=0,

②cha[i-1]>0&&cha[i]<0.

对于贡献开始增加的临界值x是:

ll now=0;
if(cha[L-1]>0)now+=cha[L-1];
if(cha[L]<0)now-=cha[L];

对于第一张种情况贡献始终是大于等于0的。

对于第二种情况,是存在贡献为负的,之后贡献一直增加,但是我们都可简单推论,只有当询问区间L==R的时候才可能取到贡献为负的情况,不然肯定是取第一种情况的位置。

因此我们用线段树记录cha[i-1]与cha[i]对答案贡献的临界值,若l==r则直接计算答案,否则找到区间的最小值,若临界值的最小值是>x则sum不变,否则sum=sum+(x-mi)*2.

对于第二种操作,其实就只是改变了cha[l-1]与cha[r],线段树更新即可。

AC代码:

#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
typedef long long ll;
ll a[100005],cha[100005];
ll tree[100005*4],n;
void update(ll pos,ll L,ll R,ll root)
{
	if(L==R)
	{
		ll now=0;
		if(cha[L-1]>0)now+=cha[L-1];
		if(cha[L]<0)now-=cha[L];
		tree[root]=now;
		return ;
	}
	ll mid=L+R>>1;
	if(pos<=mid)update(pos,L,mid,root<<1);
	else update(pos,mid+1,R,root<<1|1);
	tree[root]=min(tree[root<<1],tree[root<<1|1]); 
}
ll query(ll l,ll r,ll L,ll R,ll root)
{
	if(l<=L&&R<=r)return tree[root];
	ll mid=L+R>>1;
	if(r<=mid)return query(l,r,L,mid,root<<1);
	else if(l>mid)return query(l,r,mid+1,R,root<<1|1);
	else return min(query(l,mid,L,mid,root<<1),query(mid+1,r,mid+1,R,root<<1|1));
}
int main()
{
	scanf("%lld",&n);
	ll sum=0;
	for(ll i=0;i<n;i++)
		scanf("%lld",&a[i]);
	for(ll i=0;i<n-1;i++)
	{
		cha[i]=a[i]-a[i+1],sum+=abs(cha[i]);
		if(i>0)update(i,1,n-2,1);
	}
	ll q;
	scanf("%lld",&q);
	while(q--)
	{
		ll op,l,r,x;
		scanf("%lld%lld%lld%lld",&op,&l,&r,&x);
		l--,r--;
		if(op==1)
		{
			if(l==r)
			{
				printf("%lld\n",sum-abs(cha[l-1])-abs(cha[l])+abs(cha[l-1]-x)+abs(cha[l]+x));
				continue;
			}
			ll s=query(l,r,1,n-2,1);
			if(s>x)printf("%lld\n",sum);
			else printf("%lld\n",sum+(x-s)*2);
		}
		else 
		{
			sum=sum-abs(cha[l-1])-abs(cha[r]);
			cha[l-1]-=x;
			cha[r]+=x;
			sum=sum+abs(cha[l-1])+abs(cha[r]);
			update(l,1,n-2,1);
			update(r,1,n-2,1);
			if(l-1>=1)update(l-1,1,n-2,1);
			if(r+1<=n-2)update(r+1,1,n-2,1);
		}
	}
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值