HDU6638 2019多校第六场 Snowy Smile (坐标离散化+线段树枚举维护区间最大子段和)

题意

T组,给n,下面n行 给出个n个点的横纵坐标和这一点的价值(有正有负),点数最多2000,坐标范围(-1e9~1e9)问你在图中框出一个矩形(包括边界)得到的最大价值是多少,输出MaxValue。

思路

因为坐标范围给出-1e9~1e9,对所有点的纵坐标离散化,再将所有点按照横坐标从小到大排序,离散化后枚举n个点,每次遇到下一个不同的x坐标时(所选矩形左边界修改),重新build线段树(此处由上一步离散化得知不同y坐标有多少个——要建多少个点),然后对于当前左边界不断扩展矩形右边界,每次对当前右边界线上的点统统压入线段树,线段树维护区间最大子段和,每次维护矩形内的MaxValue。好的,下面来看一下怎么离散化还有维护最大子段和……^_^

坐标离散化

就把差距很大的一堆坐标改成相邻的QAQ没啥说的这是纵坐标离散化代码

int m = 0,M = 0;
scanf("%d",&n);
for(int i = 1;i <= n; i++){
	scanf("%d %d %d",&a[i].x,&a[i].y,&a[i].w);
	b[++m] = a[i].y;
}
sort(b + 1,b + 1 + m);
for(int i = 1,M = 0;i <= m; i++){
	if(i == 1 || b[i] != b[M]) b[++M] = b[i];
}
sort(a + 1,a + 1 + n,cmp);//横坐标从小到大
for(int i = 1;i <= n; i++){
	a[i].y = lower_bound(b + 1,b + 1 + M,a[i].y) - b;
}

线段树维护区间最大子段和

维护最大子段和需要这几个元素(っ•̀ω•́)っ✎⁾⁾ 

s——区间和

ls——区间左半边的最大子段和

rs——区间右半边的最大子段和

as——区间最大子段和

在维护的过程中,存在以下转换:

s——就是s求和喽hahaha( ̄. ̄);

ls——max(左儿子的ls,左儿子.s+右儿子.ls);

rs——max(右儿子的rs,右儿子.s+左儿子.rs);

as——max(左儿子.rs+右儿子.ls,max(左儿子.as,.右儿子as));

不懂的话画个图图就ok啦(<ゝω・)☆

复杂度

坐标离散化O(n)   维护O(n^2 log n)

代码实现(・ω<)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>

using namespace std;
const int MaxN = 2005;
typedef long long LL;

int t,n;
int b[MaxN];
LL ans;

struct NODE{
	int x,y,w;
}a[MaxN];

struct NN{
	LL s,ls,rs,as,l,r;
}tree[MaxN * 4];

bool cmp(NODE A,NODE B){
	return A.x < B.x;
}

void build(int k,int ll,int rr){
	tree[k].s = tree[k].ls = tree[k].rs = tree[k].as = 0;
	tree[k].l = ll;
	tree[k].r = rr;
	if(ll == rr) return;
	int mid = (ll + rr) / 2;
	build(k * 2,ll,mid);
	build(k * 2 + 1,mid + 1,rr);
}

void push_up(int k){
	tree[k].s = tree[k * 2].s + tree[k * 2 + 1].s;
	tree[k].ls = max(tree[k * 2].ls,tree[k * 2 + 1].ls + tree[k * 2].s);
	tree[k].rs = max(tree[k * 2 + 1].rs,tree[k * 2].rs + tree[k * 2 + 1].s);
	tree[k].as = max(tree[k * 2].rs + tree[k * 2 + 1].ls,max(tree[k * 2].as,tree[k * 2 + 1].as));
}

void chan_p(int k,int pos,int ad){
	if(tree[k].l == tree[k].r){
		tree[k].s += ad;
		tree[k].ls += ad;
		tree[k].rs += ad;
		tree[k].as += ad;
		return ;
	}
	int mid = (tree[k].l + tree[k].r) / 2;
	if(pos <= mid) chan_p(2 * k,pos,ad);
	else chan_p(2 * k + 1,pos,ad);
	push_up(k);//向上反馈哦~
}

int main()
{
	scanf("%d",&t);
	while(t--){
		int M = 0;
		scanf("%d",&n);
		for(int i = 1;i <= n; i++){
			scanf("%d %d %d",&a[i].x,&a[i].y,&a[i].w);
			b[i] = a[i].y;
		}
		sort(b + 1,b + 1 + n);
		for(int i = 1;i <= n; i++){
			if(i == 1 || b[i] != b[M]) b[++M] = b[i];
		}
		sort(a + 1,a + 1 + n,cmp);//横坐标从小到大
		for(int i = 1;i <= n; i++){
			a[i].y = lower_bound(b + 1,b + 1 + M,a[i].y) - b;
		}
		ans = 0;
		int k = 0;

		for(int i = 1;i <= n; i++){
			if(i == 1 || a[i].x != a[i - 1].x){//左边界修改
				build(1,1,M);//M个不同纵坐标 清空
				for(int j = i;j <= n; j = k){
					for(k = j;k <= n && a[j].x == a[k].x; k++){
						//扩展的右边界上的所有点压入线段树
						chan_p(1,a[k].y,a[k].w);
					}
					ans = max(ans,tree[1].as);
				}
			}
		}
		printf("%lld\n",ans);
	}
}
发布了31 篇原创文章 · 获赞 5 · 访问量 1381

猜你喜欢

转载自blog.csdn.net/qq_43685900/article/details/98954221