HDU6638

题意:给出一些点,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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值