hdu 6638 2019多校训练六

给n个坐标,每个坐标有个权值(可小于零),问如何取矩形使得矩形内的权值和最大。

题解:

首先将纵坐标离散化到 O(n) 的范围内,方便后续的处理。 将所有点按照横坐标排序,枚举矩形的上边界,然后往后依次加入每个点,这样就确定了 矩形的上下边界。设 v[y] 表示矩形内部纵坐标为 y 的点的权值和,则答案为 v 的最大子段和, 用线段树维护带修改的最大子段和即可。 时间复杂度 O(n2logn)。

自从学了线段树的区间合并以来今天是第一次遇到区间合并的题

【代码】


#include<bits/stdc++.h>
using namespace std;
const int N=2e3+10;
typedef long long ll;
struct node
{
int x,y;
ll w;
}a[N];
struct node1
{
int x;
ll w;
};
int n;
int X[N],Y[N],tot1,tot2;
vector<node1>G[N];
ll ls[N*4],rs[N*4],mx[N*4],sum[N*4];
void pushup(int id,int l,int r)
{
ls[id]=max(ls[id<<1],sum[id<<1]+ls[id<<1|1]);
rs[id]=max(rs[id<<1|1],sum[id<<1|1]+rs[id<<1]);

sum[id]=sum[id<<1|1]+sum[id<<1];

mx[id]=max(mx[id<<1],mx[id<<1|1]);
mx[id]=max(mx[id],rs[id<<1]+ls[id<<1|1]);
mx[id]=max(mx[id],sum[id<<1|1]+rs[id<<1]);
mx[id]=max(mx[id],ls[id]);
mx[id]=max(mx[id],rs[id]);
}
void up(int id,int l,int r,int pos,ll w)
{
if(l==r)
{
sum[id]+=w;
ls[id]+=w;
rs[id]+=w;
mx[id]+=w;
return ;
}
int mid=l+r>>1;
if(pos<=mid) up(id<<1,l,mid,pos,w);
else up(id<<1|1,mid+1,r,pos,w);
pushup(id,l,r);
}
int main()
{
int _;
cin>>_;
while(_--){
scanf("%d",&n);
for(int i=1;i<=n;++i) G[i].clear();
for(int i=1;i<=n;++i)
{
scanf("%d%d%lld",&a[i].x,&a[i].y,&a[i].w);
X[i]=a[i].x,Y[i]=a[i].y;
}
sort(X+1,X+1+n);
sort(Y+1,Y+1+n);
tot1=unique(X+1,X+1+n)-X-1;
tot2=unique(Y+1,Y+1+n)-Y-1;
for(int i=1;i<=n;++i)
{
a[i].x=lower_bound(X+1,X+1+tot1,a[i].x)-X;
a[i].y=lower_bound(Y+1,Y+1+tot2,a[i].y)-Y;
G[a[i].y].push_back({a[i].x,a[i].w});
}
ll ans=0;
for(int i=1;i<=tot2;++i)//枚举下边界
{
for(int j=0;j<=4*tot1;++j)
ls[j]=rs[j]=mx[j]=sum[j]=0;
for(int j=i;j>=1;--j)//枚举上边界
{
for(node1 v:G[j])//对于这一行有多少个点
up(1,1,tot1,v.x,v.w);
ans=max(ans,mx[1]);
}
}
printf("%lld\n",ans);

}
}