【线段树】Educational Codeforces Round 73 (Rated for Div. 2) - F. Choose a Square

题目链接https://codeforc.es/problemset/problem/1221/F


题意

二维平面内有n个带权值的点,要求出一个两点在直线 y = x y=x y=x上的正方形,使得覆盖点的权值之和减去正方形的边长最大。


题解

假设正方形的坐标为 ( a x , a x ) (ax,ax) (ax,ax) ( a y , a y ) (ay,ay) (ay,ay),如果点 ( x , y ) (x,y) (x,y)在正方形内,那么 a x < = m i n ( x , y ) ax<=min(x,y) ax<=min(x,y)并且 m a x ( x , y ) < = a y max(x,y)<=ay max(x,y)<=ay
这样考虑的话每个点就转化成一个区间,现在是要求一段大区间使得包含的小区间权值之和减去大区间长度最大。

很显然答案一定是在小区间端点上的,所以把小区间按照右端点排个序,枚举右端点,把每个区间的权值在线段树上更新,线段树上维护最大值和位置就行了。
线段树初始可以给每个点赋一个递增的负值,这样就能维护长度的答案了。


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double db;
typedef pair<ll,int>piir;
const int N=2e6+7;
const ll inf=1e18;
int n;
ll lz[N<<2];
piir t[N<<2];
int b[N],tot;
int ax,ay;
ll ans;
struct Node{
    int x,y,c;
    bool operator<(const Node k)const{return y<k.y;}
}a[N];
void bd(int rt,int l,int r){
    lz[rt]=0;
    if(l==r){
        t[rt].first=b[l]-b[tot];
        t[rt].second=l;
        return;
    }
    int m=l+r>>1;
    bd(rt<<1,l,m);
    bd(rt<<1|1,m+1,r);
    if(t[rt<<1].first>t[rt<<1|1].first) t[rt]=t[rt<<1];
    else t[rt]=t[rt<<1|1];
}
void pd(int rt){
    if(lz[rt]){
        t[rt<<1].first+=lz[rt];
        t[rt<<1|1].first+=lz[rt];
        lz[rt<<1]+=lz[rt];
        lz[rt<<1|1]+=lz[rt];
        lz[rt]=0;
    }
}
void upd(int rt,int l,int r,int L,int R,ll val){
    if(L<=l&&r<=R){t[rt].first+=val;lz[rt]+=val;return;}
    pd(rt);
    int m=l+r>>1;
    if(L<=m) upd(rt<<1,l,m,L,R,val);
    if(m<R) upd(rt<<1|1,m+1,r,L,R,val);
    if(t[rt<<1].first>t[rt<<1|1].first) t[rt]=t[rt<<1];
    else t[rt]=t[rt<<1|1];
}
piir que(int rt,int l,int r,int L,int R){
    if(L<=l&&r<=R) return t[rt];
    pd(rt);
    int m=l+r>>1;
    piir le=piir(-inf,0),ri=piir(-inf,0);
    if(L<=m) le=que(rt<<1,l,m,L,R);
    if(m<R) ri=que(rt<<1|1,m+1,r,L,R);
    return (le.first>ri.first)?le:ri;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].c);
        if(a[i].x>a[i].y) swap(a[i].x,a[i].y);
        b[++tot]=a[i].x;
        b[++tot]=a[i].y;
    }
    sort(b+1,b+1+tot);
    tot=unique(b+1,b+1+tot)-b-1;
    for(int i=1;i<=n;i++){
        a[i].x=lower_bound(b+1,b+1+tot,a[i].x)-b;
        a[i].y=lower_bound(b+1,b+1+tot,a[i].y)-b;
    }
    bd(1,1,tot);
    sort(a+1,a+1+n);
    for(int i=1;i<=n;i++){
        upd(1,1,tot,1,a[i].x,a[i].c);
        if(i<n&&a[i+1].y==a[i].y) continue;
        piir tmp=que(1,1,tot,1,a[i].y);
        if(tmp.first+b[tot]-b[a[i].y]>ans){
            ans=tmp.first+b[tot]-b[a[i].y];
            ax=tmp.second;
            ay=a[i].y;
        }
    }
    printf("%lld\n",ans);
    b[tot+1]=-1;
    if(ans==0){
        for(int i=1;i<=tot;i++){
            if(b[i]==b[i+1]) continue;
            if(b[i]+1!=b[i+1]){
                printf("%d %d %d %d\n",b[i]+1,b[i]+1,b[i]+1,b[i]+1);
                break;
            }
        }
    }
    else printf("%d %d %d %d\n",b[ax],b[ax],b[ay],b[ay]);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值