2019 Multi-University Training Contest 6 1005 Snowy Smile —— 线段树

This way

题意:

空间中有n个点,让你选择一个矩形覆盖一些点或者不覆盖任何点使得这个矩形中所有点的和最大。

题解:

虽然貌似它是个2000*2000的矩阵,但是它有的点只有2000个。所以我们先按照x轴排序,然后枚举左端点的同时枚举右端点,意思是固定左右端点,一定包括这两个点的最大子矩阵是多少。用线段树维护区间前缀和,然后查上面的最大前缀和-下面的最小前缀和,就像下图:
在这里插入图片描述
但是有一个很重要的剪枝,也就是在每次查询之前看看mx[1]-max(0ll,mi[1])是否小于等于ans,如果是的话就没必要往下做了。虽然不严谨,但是对于答案是不会有影响的。

#include<bits/stdc++.h>
//#pragma GCC optimize(2)
using namespace std;
#define ll long long
const int N=2e3+5;
const ll inf=1e18;
int dis_x[N],dis_y[N];
ll mi[N*4],mx[N*4],flag[N*4],pre[N*4];
struct node
{
    int x,y;
    ll w;
    bool operator< (const node& a)const
    {
        return x<a.x;
    }
}p[N];
vector<node>have[N];
void push_down(int root)
{
    if(flag[root]==inf)
        return ;
    mi[root<<1]+=flag[root];
    mi[root<<1|1]+=flag[root];
    mx[root<<1]+=flag[root];
    mx[root<<1|1]+=flag[root];
    flag[root<<1]+=flag[root];
    flag[root<<1|1]+=flag[root];
    flag[root]=0;
}
void update(int l,int r,int root,int ql,int qr,ll v)
{
    if(l>=ql&&r<=qr)
    {
        mi[root]+=v;
        mx[root]+=v;
        flag[root]+=v;
        return ;
    }
    push_down(root);
    int mid=l+r>>1;
    if(mid>=ql)
        update(l,mid,root<<1,ql,qr,v);
    if(mid<qr)
        update(mid+1,r,root<<1|1,ql,qr,v);
    mi[root]=min(mi[root<<1],mi[root<<1|1]);
    mx[root]=max(mx[root<<1],mx[root<<1|1]);
}
ll q_mi(int l,int r,int root,int ql,int qr)
{
    if(l>=ql&&r<=qr)
        return mi[root];
    push_down(root);
    int mid=l+r>>1;
    ll ans=inf;
    if(mid>=ql)
        ans=q_mi(l,mid,root<<1,ql,qr);
    if(mid<qr)
        ans=min(ans,q_mi(mid+1,r,root<<1|1,ql,qr));
    return ans;
}
ll q_mx(int l,int r,int root,int ql,int qr)
{
    if(l>=ql&&r<=qr)
        return mx[root];
    push_down(root);
    int mid=l+r>>1;
    ll ans=-inf;
    if(mid>=ql)
        ans=q_mx(l,mid,root<<1,ql,qr);
    if(mid<qr)
        ans=max(ans,q_mx(mid+1,r,root<<1|1,ql,qr));
    return ans;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d%d%lld",&p[i].x,&p[i].y,&p[i].w),dis_x[i]=p[i].x,dis_y[i]=p[i].y,have[i].clear();
        sort(dis_x+1,dis_x+1+n);
        sort(dis_y+1,dis_y+1+n);
        int all_x=unique(dis_x+1,dis_x+1+n)-dis_x-1;
        int all_y=unique(dis_y+1,dis_y+1+n)-dis_y-1;
        for(int i=1;i<=n;i++)
        {
            p[i].x=lower_bound(dis_x+1,dis_x+1+all_x,p[i].x)-dis_x;
            p[i].y=lower_bound(dis_y+1,dis_y+1+all_y,p[i].y)-dis_y;
            have[p[i].x].push_back(p[i]);
        }
        sort(p+1,p+1+n);
        ll ans=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=4*all_y;j++)
                mi[j]=0,mx[j]=0,flag[j]=0;

            for(int j=i;j<=n;j++)
            {
                if(p[j].x==p[j-1].x&&p[j].y==p[j-1].y)
                    continue;
                if(p[j].x!=p[j-1].x||j==i)
                    for(auto v:have[p[j].x])
                        update(1,all_y,1,v.y,all_y,v.w);
                q_mi(1,all_y,1,1,all_y);
                if(mx[1]-min(0ll,mi[1])<=ans)
                    continue;
                int l=min(p[i].y,p[j].y),r=max(p[i].y,p[j].y);
                ll maxn=q_mx(1,all_y,1,r,all_y);
                ll minn=l-1>0?q_mi(1,all_y,1,1,l-1):0ll;
                if(minn<=0)
                    ans=max(ans,maxn-minn);
                else
                    ans=max(ans,maxn);
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}
/*
10
7
1 1 -1000000000
1 2 2000000000
1 3 3000000000
2 2 -2000000000
2 3 -2000000000
3 2 4000000000
3 3 5000000000
10
8
1 3 20
1 5 -30
1 7 10
5 5 60
5 2 -140
5 1 120
4 1 -130
2 8 -111


8
1 3 40
1 5 -30
1 7 10
5 5 60
5 2 -140
5 1 120
4 1 -130
2 8 -111

*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值