【树状数组or线段树】中山纪念中学暑期游Day13——pot

前言

超级毒瘤数据结构题!

确认过眼神,是我搞不出来的题

题目

这个假期,小h在自家院子里种了许多花,它们围成了一个圈,从1..n编号(n<=100000),小h 对每盆花都有一个喜好值xi,(-1000<=xi<=1000),小h现在觉得这样一成不变很枯燥,于是他做了m(m<=100000)个改动,每次把第ki盘花改成喜好值为di的花,然后小h要你告诉他,在这个花圈中,连续的最大喜好值是多少。

Input

第一行,n,花盆的数量
第二行,n个数,表示对于每盆花的喜好值。
第三行:m, 改动的次数
以下m行,每行两个数ki 和di 。

Output

M行,每一行对应一个更改,表示连续的最大喜好值,且不能整圈都选。(注意:是在圈上找)

Sample Input

5
3 -2 1 2 -5
4
2 -2
5 -5
2 -4
5 -1

Sample Output

4
4
3
5

分析

毒瘤数据结构题,方法更新了好多次(“维护5个东西”的那个方法好像有Bug,不清楚),最后大约如下:

思路:

利用整体减空白的想法

找到区间中最小的一段,

然后用整体减去他就能得到最大字段和了

如果遇到了选了整段的情况,就减一个最小的数就行了

维护7个东西:(但是我自己觉得很奇怪)

1.最大前缀和;2.最小前缀和;3.最大后缀和;4.最小后缀和;5.最大子段和;6.最小子段和;7.总和

然后就是“合并”的问题了,自己想想吧2333....


维护5个东西”的那个方法好像有Bug,老师讲完感觉不对,出了个数据,果真把自己hack了

例如:9 -5 -5 11,

最大前缀 = 9-5-5+11 = 10,然而如果只取11,答案会更优,所以emmm...好像说是方法有点问题?

最后他们说取个什么min,没听清... ...反正我现在很混乱...


贴一个LZC大佬的讲解...

T3:
考虑维护序列的最大子段和和最小子段和,答案要么是最大子段和,要么是序列的和 - 最小子段和,这样就能考虑到环的情况。
可以用线段树维护,注意不能全部都选!
这样的话,可以用线段树维护区间的答案,然后输出  的答案和  的答案取个 max,这样就不会都选了。
哦听说有人不会用线段树维护最大 / 最小子段和?那我来补一下。
只说最大的,最小的类似。
在线段树每个结点上维护这个区间的和、最大前缀和、最大后缀和、最大子段和。
那么合并儿子信息的时候,区间和很简单;最大前缀和有两种选择,一种是左儿子的区间和 + 右儿子的最大前缀和,一种是左儿子的最大前缀和;后缀和类似;最大子段和要么是左儿子的最大子段和,要么是右儿子的最大子段和,要么是左儿子的最大后缀和 + 右儿子的最大前缀和。
然后如果还要询问区间的话就类似地拼一下,就是代码长了点(对于我这种长期写树套树的数据结构选手来说还好还好)


另一个大佬:https://blog.csdn.net/ha_ing/article/details/99462749

简要思路:本题是要在一个环上维护最大区间和,但区间不能包括整个环。考虑到一个环可能会从最大和区间断开,我们还可以维护一个最小区间和(整个数列由一个最大和区间跟一个最小和区间构成,可用反证法证明,并且环的断点只可能断开两个区间中的一个),用环的总值减去最小区间和,得到另一个预选答案,将两者比较输出较大值即可。
区间维护要利用区间合并,具体方法见代码吧。
本题还要特判,当环上所有数为正数时,我们维护的最大区间和恰好等于环的总值,此时我们维护的最小区间和也正好为环上最小的一个数,此时答案只能用环的总值减去最小区间和得出。

AC代码

我蒟蒻,自己打不来qwq... ...

特别鸣谢:https://blog.csdn.net/ha_ing/article/details/99462749

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ls pos<<1
#define rs pos<<1|1
using namespace std;
const int MAXN=1e5,INF=0x3f3f3f3f;
int n,m;
struct segment_tree
{
	//意义根据单词就可以猜出来了
	//lmax:包括最左端的最大区间和,rmax:包括最右端的最大区间和,midmax:数列范围内最大区间和,min同理
	int sum[MAXN*4+5];
	int lmax[MAXN*4+5],rmax[MAXN*4+5],lmin[MAXN*4+5],rmin[MAXN*4+5];
	int midmax[MAXN*4+5],midmin[MAXN*4+5];
	void update(int pos)
	{
		sum[pos]=sum[ls]+sum[rs];
		lmax[pos]=max(lmax[ls],sum[ls]+lmax[rs]);
		rmax[pos]=max(rmax[rs],sum[rs]+rmax[ls]);
		lmin[pos]=min(lmin[ls],sum[ls]+lmin[rs]);
		rmin[pos]=min(rmin[rs],sum[rs]+rmin[ls]);
		//合并右儿子的lmax与左儿子的rmax可得到父亲节点的midmax :
		midmax[pos]=max(lmax[rs]+rmax[ls],max(midmax[ls],midmax[rs]));
		midmin[pos]=min(lmin[rs]+rmin[ls],min(midmin[ls],midmin[rs]));
		return ;
	}
	void pre(int pos,int l,int r)
	{
		sum[pos]=lmax[pos]=rmax[pos]=midmax[pos]=-INF;
		lmin[pos]=rmin[pos]=midmin[pos]=INF;
		if(l==r)
			return ;
		int mid=(l+r)/2;
		pre(ls,l,mid);
		pre(rs,mid+1,r);
		return ;
	}
	void change(int pos,int aim,int val,int l,int r)
	{
		if(l==r)
		{
			sum[pos]=lmin[pos]=lmax[pos]=rmin[pos]=rmax[pos]=midmin[pos]=midmax[pos]=val;
			return ;
		}
		int mid=(l+r)/2;
		if(mid>=aim)
			change(ls,aim,val,l,mid);
		else
			change(rs,aim,val,mid+1,r);
		update(pos);
		return ;	
	}
}tree;//用结构体封装线段树
int main()
{
	scanf("%d",&n);
	tree.pre(1,1,n);
	int tmp;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&tmp);
		tree.change(1,i,tmp,1,n);
	}
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
	{
		int k,d;
		scanf("%d%d",&k,&d);
		tree.change(1,k,d,1,n);
		if(tree.sum[1]==tree.midmax[1])//不能包括整个环 
			printf("%d\n",tree.sum[1]-tree.midmin[1]);
		else
			printf("%d\n",max(tree.sum[1]-tree.midmin[1],tree.midmax[1]));
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值