题意:给出一些点,x轴坐标、y轴坐标和该点加值。要求找到一个矩形,这个矩形内点的价值和最大。
思路:点数少于等于2000,x、y坐标范围在-1e9——1e9,所以离散化处理到2000*2000,并且有用的点只有200个。
首先将纵坐标离散化到 O(n) 的范围内,方便后续的处理。
将所有点按照横坐标排序,枚举矩形的上边界,然后往后依次加入每个点,这样就确定了矩形的上下边界。
设 v[x] 表示矩形内部横坐标为 x 的点的权值和,则答案为 v 的最大子段和,
用线段树维护带修改的最大子段和即可。
时间复杂度 O(n*n* log n)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=2005;
ll n,x[maxn],y[maxn],w[maxn];
ll a[maxn],b[maxn],mp[maxn][maxn];
vector<ll> v[maxn];
struct node{
ll l,r,w,vl,vr,mx;
}t[maxn<<2];
void update(ll k)
{
t[k].w=t[k<<1].w+t[k<<1|1].w;
t[k].vl=max(t[k<<1].vl,t[k<<1].w+t[k<<1|1].vl);
t[k].vr=max(t[k<<1|1].vr,t[k<<1].vr+t[k<<1|1].w);
t[k].mx=max(max(t[k<<1].mx,t[k<<1|1].mx),t[k<<1].vr+t[k<<1|1].vl);
}
void build(ll k,ll l,ll r)
{
t[k].l=l;t[k].r=r;
if(l==r){
t[k].w=t[k].vl=t[k].vr=t[k].mx=0;
return ;
}
ll mid=(t[k].l+t[k].r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
update(k);
}
void add(ll k,ll x,ll y)
{
if(t[k].l==t[k].r){
t[k].w+=y;
t[k].vl+=y;
t[k].vr+=y;
t[k].mx+=y;
return ;
}
ll mid=(t[k].l+t[k].r)>>1;
if(x<=mid) add(k<<1,x,y);
else add(k<<1|1,x,y);
update(k);
}
int main()
{
int tt;
scanf("%d",&tt);
while(tt--){
memset(mp,0,sizeof(mp));
scanf("%lld",&n);
for(ll i=1;i<=n;i++){
scanf("%lld %lld %lld",&x[i],&y[i],&w[i]);
a[i]=x[i];b[i]=y[i];
}
sort(a+1,a+n+1);sort(b+1,b+n+1);
ll lena=unique(a+1,a+n+1)-a-1,lenb=unique(b+1,b+n+1)-b-1;
for(ll i=1;i<=lenb;i++) v[i].clear();
for(ll i=1;i<=n;i++){
x[i]=lower_bound(a+1,a+1+lena,x[i])-a;
y[i]=lower_bound(b+1,b+1+lenb,y[i])-b;
mp[y[i]][x[i]]+=w[i];
}
for(ll i=1;i<=lenb;i++){
for(ll j=1;j<=lena;j++){
if(mp[i][j]) v[i].push_back(j);
}
}
ll ans=0;
for(ll i=1;i<=lenb;i++){
build(1,1,lena);
for(ll j=i;j<=lenb;j++){
for(ll k=0;k<v[j].size();k++){
add(1,v[j][k],mp[j][v[j][k]]);
}
ans=max(ans,t[1].mx);
}
}
printf("%lld\n",ans);
}
return 0;
}