首页 > 其他分享 >poj2750(线段树+复杂区间合并)

poj2750(线段树+复杂区间合并)

时间:2023-04-15 18:24:06浏览次数:47  
标签:pre suf lc 合并 线段 tr include mx poj2750

Potted Flower

POJ - 2750

<iframe frameborder="0" height="2048px" id="frame-description" scrolling="no" src="https://vjudge.csgrandeur.cn/problem/description/1630311985?1663497294000" style="box-sizing: inherit; height: 1497.42px" width="100%"></iframe>

思路:我们将题目简单化,假设我们要求的是序列的最大连续子段和,且可以包括所有数。

我们的线段树需要维护这段区间的最大前缀和pre,最大后缀和suf,区间和sum,区间连续最大和mx

那么难点就在于如何由子节点更新父节点。

我们可以知道,tr[p].sum = tr[lc].sum + tr[rc].sum //p为父节点,lc为左孩子,rc为右孩子

tr[p].pre = max(tr[lc].pre,tr[lc].sum+tr[rc].pre) //左孩子的pre与左孩子的区间和加右孩子的pre取最大值

同理:tr[p].suf = max(tr[rc].suf,tr[rc].sum+tr[lc].suf)

那么如何更新区间最大和mx呢?

我们有:tr[p].mx = max(tr[lc].mx , tr[rc].mx ) //我们在左右孩子的mx中取最大值

但如果最大区间和在中间呢?

可以这样写

tr[p].mx = max(tr[p].mx , tr[lc].suf+ tr[rc].pre) //我们维护pre和suf就是为了解决中间的情况

我们已经解决了序列的最大连续字段和了,但是题目要求的是环,如何求?

假设我们知道了这段序列的连续最小和mi (跟mx的意思反一下,求得是最小)

那么我们用总的区间和sum减去 mi 的结果再与mx取最大就是结果

例:1 -3 -2 3 5 这个例子中mx为8 (3+5),mi 为-5 (-3 + (- 2) ) , sum为4 结果为 max( 8, 4 - (-5)) = 9,(因为是环,所以为 1 + 5 + 3)

求区间连续最小和mi与求最大和mx是一样的,最小前缀和pre_1,最小后缀和suf_1

#define _CRT_SECURE_NO_WARNINGS 1
#include<algorithm>
#include<fstream>
#include<iostream>
#include<cstdio>
#include<deque>
#include<string>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<bitset>
//#include<unordered_map>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 310000
#define N 2100100
#define endl '\n'
#define exp 1e-8
#define lc p << 1
#define rc p << 1|1
#define lowbit(x) ((x)&-(x))
const double pi = acos(-1.0);
typedef long long LL;
typedef unsigned long long ULL;
inline LL read() {
	LL x = 0, f = 1;
	char ch = getchar();
	while (ch < '0' || ch>'9') {
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 1) + (x << 3) + (ch ^ 48);
		ch = getchar();
	}
	return x * f;
}
struct tree
{
	int l, r, pre, suf,mx,sum,pre_1,suf_1,mi;
}tr[4*N];
int arr[N],n,m;
void pushup(int p)  //核心代码
{
	tr[p].sum = tr[lc].sum + tr[rc].sum;

	tr[p].pre = max(tr[lc].pre, tr[lc].sum + tr[rc].pre);  //求最大
	tr[p].suf = max(tr[rc].suf, tr[lc].suf + tr[rc].sum);
	tr[p].mx = max(tr[lc].mx, tr[rc].mx);
	tr[p].mx = max(tr[p].mx, tr[lc].suf + tr[rc].pre);
        //tr[p].mx = max(tr[p].mx, tr[p].sum) 可以不写这行代码,想想为什么

	tr[p].pre_1 = min(tr[lc].pre_1, tr[lc].sum + tr[rc].pre_1);  //求最小
	tr[p].suf_1 = min(tr[rc].suf_1, tr[rc].sum + tr[lc].suf_1);
	tr[p].mi = min(tr[lc].mi, tr[rc].mi );
	tr[p].mi = min(tr[p].mi, tr[lc].suf_1 + tr[rc].pre_1);
}
void build(int p, int l, int r)
{
	tr[p].l = l, tr[p].r = r;
	if (l == r) {tr[p].pre = tr[p].suf = tr[p].mx = tr[p].sum = tr[p].pre_1 = tr[p].suf_1 = tr[p].mi = arr[l]; return; }
	int m = l + r >> 1;
	build(lc, l, m);
	build(rc, m + 1, r);
	pushup(p);
}
void update(int p, int x, int k) //单点修改
{
	if (tr[p].l == tr[p].r)
	{
		tr[p].pre = tr[p].suf = tr[p].mx = tr[p].sum = tr[p].pre_1 = tr[p].suf_1 = tr[p].mi = k;
		return;
	}
	int m = tr[p].l + tr[p].r >> 1;
	if (x <= m)update(lc, x, k);
	else update(rc, x, k);
	pushup(p);
}
int main()
{
	n = read();
	for (int i = 1; i <= n; i++)
	{
		arr[i] = read();
	}
	build(1, 1, n);
	m = read();
	while (m--)
	{
		int a = read(), b = read();
		update(1, a, b);
		if (tr[1].mx == tr[1].sum)  //因为题目说不能连续n个,所以当mx == sum时,我们要减去最小的
			cout << tr[1].sum - tr[1].mi <<endl;
		else 
		    cout << max(tr[1].mx, (tr[1].sum - tr[1].mi)) <<endl; 
	}
	return 0;
}

标签:pre,suf,lc,合并,线段,tr,include,mx,poj2750
From: https://www.cnblogs.com/wyh344866/p/17321578.html

相关文章

  • 删除无效的括号(广度优先搜索、字符串)、计算右侧小于当前元素的个数(树状数组、线段
    删除无效的括号(广度优先搜索、字符串)给你一个由若干括号和字母组成的字符串s,删除最小数量的无效括号,使得输入的字符串有效。返回所有可能的结果。答案可以按任意顺序返回。示例1:输入:s="()())()"输出:["(())()","()()()"]示例2:输入:s="(a)())()"输出:["(a())()","(......
  • 【230414-3】有3种不同颜色的灯泡,要在如图所示的6个点A、B、C,A',B‘,C’上各安装1个灯
    ......
  • poj2777(线段树)
    CountColorPOJ-2777思路:暴力能过,线段树维护这个区间的颜色,如果是混色则置为1,如果是单一颜色则设为这个颜色,修改就是正常的区间修改,区间查询就要变一下。还有题解是用二进制做得,可以学一下。#define_CRT_SECURE_NO_WARNINGS1#include<algorithm>#include<fstream>#inc......
  • Java中常用排序算法及示例-冒泡排序、希尔排序、选择排序、插入排序、合并排序、基数
    场景Java中需要对数据进行排序处理,常用的排序算法以及示例进行归纳整理。注:博客:https://blog.csdn.net/badao_liumang_qizhi实现1、冒泡排序冒泡排序法又称为交换排序法,原理是从第一个元素开始,比较相邻元素的大小,若大小顺序有误,则对调后再进行下一个元素的比较。如此扫描......
  • 线段树(单点修改,区间查询)
    题目描述如题,已知一个数列,你需要进行下面两种操作:将某一个数加上 x求出某区间每一个数的和输入格式第一行包含两个正整数 n,m,分别表示该数列数字的个数和操作的总个数。第二行包含 n 个用空格分隔的整数,其中第 i 个数字表示数列第 i 项的初始值。接下来 ......
  • POJ 2299 Ultra-QuickSort(线段树+离散化)
    题目地址:POJ2299这题曾经用归并排序做过,线段树加上离散化也可以做。一般线段树的话会超时。这题的数字最大到10^10次方,显然太大,但是可以利用下标,下标总共只有50w。可以从数字大的开始向树上加点,然后统计下标比它小即在它左边的数的个数。因为每加一个数的时候,比该数大的数已经加完......
  • POJ 3468 A Simple Problem with Integers(线段树区间更新)
    题目地址:POJ3468打了个篮球回来果然神经有点冲动。。无脑的狂交了8次WA。。居然是更新的时候把r-l写成了l-r。。。这题就是区间更新裸题。区间更新就是加一个lazy标记,延迟标记,只有向下查询的时候才将lazy标记向下更新。其他的均按线段树的来就行。代码如下:#include<iostream>#in......
  • HDU 1166 敌兵布阵(线段树)
    题目地址:HDU1166听说胡浩版的线段树挺有名的。于是就拜访了一下他的博客。详情戳这里。于是就完全仿照着胡浩大牛的风格写的代码。至于原理,鹏鹏学长已经讲的再清晰不过了。我就在下面的代码注释中将原理说明一下吧。来纪念第一发线段树。下面是代码+注释。#include<iostream>#in......
  • ZOJ 3886 Nico Number (线段树)
    题目地址:ZJU3886这个题需要想到一点,因为对一个数x不断取模的话,而且设定他小于模才会进行取余操作的话,那么最多只会进行logx次,因为每次取模都会使x最少折半。然后想到了这点就很好做了。对于区间取模更新操作可以直接暴力更新,维护一个最大值,如果这个区间的最大值小于模的话,就......
  • 1、合并多个Excel表格的多个sheet到一个工作簿
    来源:https://www.zhihu.com/question/20366713/answer/1514642143一、需求描述存在两个Excel工作簿,每个工作簿有多个sheet,需要将两个工作簿中所有sheet合并到一个工作簿。二、实现新建Excel工作簿《1.xlsx》,打开该工作簿,按Alt+F11两键,调出VisualBasic界面,在左侧窗口中,右键选......