题意
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);
}
}