【poj2482】Stars in Your Window (扫描线+线段树)

Description

    长长的情书题面自动省略之……

    Here comes the problem: Assume the sky is a flat plane. All the stars lie on it with a location (x, y). for each star, there is a grade ranging from 1 to 100, representing its brightness, where 100 is the brightest and 1 is the weakest. The window is a rectangle whose edges are parallel to the x-axis or y-axis. Your task is to tell where I should put the window in order to maximize the sum of the brightness of the stars within the window. Note, the stars which are right on the edge of the window does not count. The window can be translated but rotation is not allowed.

Input

     There are several test cases in the input. The first line of each case contains 3 integers: n, W, H, indicating the number of stars, the horizontal length and the vertical height of the rectangle-shaped window. Then n lines follow, with 3 integers each: x, y, c, telling the location (x, y) and the brightness of each star. No two stars are on the same point.
    There are at least 1 and at most 10000 stars in the sky. 1<=WH<=1000000,0<=xy<231.

Output

    For each test case, output the maximum brightness in a single line.

Sample Input

3 5 4
1 2 3
2 3 2
6 3 1
3 5 4
1 2 3
2 3 2
5 3 1

Sample Output

5
6

I think

    题意:给出若干星星的坐标及亮度和一个四边均平行于坐标轴的矩形长与宽,求该矩形框内星星的最大亮度(不包括矩形边框上的星星)。
    算法:线段树+扫描线+离散化
    思路:这道题的关键在于想到把星星转化为矩形。
    一颗星星(x,y),它的亮度能够被圈进的位置范围是以(x,y),(x+w,y+h)两点连线为对角线的矩形(称该矩形为星星的矩形)。两星星的矩形相交就代表它们能被框进同一个矩形。则问题转化为求平面上矩形范围内的最大权值。
    实现:对于每一颗星星,把它以两条带权边的形式存储(x,y,y+h,c)和(x+w,y,y+h,-c),两边权互为相反数,能够保证星星的亮度被限制在自己的矩形内。用线段树存储离散化后y轴坐标区间内权值的最大值mx。使扫描线在x轴方向上移动。遇到一条边,就在线段树上更新权值最大值,不同于矩形面积交之类,线段树上的标记需要下传,每次更新后用整个区间最大值更新ans。
    由于矩形边框上的星星不能算入,在对星星存储的边排序时,要将负权边排在正权边的前面,这样就能保证先删边。
    其实题目最终就转化成为线段树区间更新求最大值的模板题了。

Code

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int sm = 10000*2+50;
typedef long long lld;
lld w,h,n,T,t,z1;
lld x1,y1,ans;
lld y[sm<<2],mx[sm<<2];
lld mk[sm<<2],add[sm<<2];
struct line {
    lld x,y1,y2,c;
}a[sm];//矩形左右边
lld Max(lld a,lld b) { return a>b?a:b; }
char ch;int f;
void read(lld &x) {
    x=0,f=1;ch=getchar();
    while(ch>'9'||ch<'0') { if(ch=='-')f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    x*=f;
}
bool cmp (line u,line v) { 
    if(u.x!=v.x) return u.x<v.x; 
    return u.c<v.c;//把负权边排在正权边的前面
}
void build(int k,int l,int r) {
    if(l+1==r) {
        mx[k]=mk[k]=add[k]=0;
        return ;
    }
    int m=(l+r)>>1;
    build(k<<1,l,m);
    build(k<<1|1,m,r);
    mx[k]=mk[k]=add[k]=0;
}
void pushdown(int k,int l,int r) {
    int m=(l+r)>>1,ls=k<<1,rs=k<<1|1;
    mk[ls]=mk[rs]=1;
    add[ls]+=add[k],add[rs]+=add[k];//是+add[k]而不是add[ls]
    mx[ls]+=add[k],mx[rs]+=add[k];
    add[k]=0,mk[k]=0;
}
void update(int k,int l,int r,line x) {
    if(x.y1<=y[l]&&y[r]<=x.y2) {
        mk[k]=1,add[k]+=x.c;
        mx[k]+=x.c;return;
    }
    if(l+1==r)return;
    if(mk[k])pushdown(k,l,r);
    int m=(l+r)>>1,ls=k<<1,rs=k<<1|1;
    if(x.y1<y[m]) update(ls,l,m,x);
    if(x.y2>y[m]) update(rs,m,r,x);
    mx[k]=Max(mx[ls],mx[rs]);
}
int main() {
    while(scanf("%lld%lld%lld",&n,&w,&h)!=EOF) {
        ans=t=0;
        for(int i=1;i<=n;++i) {
            read(x1),read(y1),read(z1);
            y[++t]=y1,a[t].x=x1;
            a[t].y1=y1,a[t].y2=y1+h,a[t].c=z1;
            y[++t]=y1+h,a[t].x=x1+w;
            a[t].y1=y1,a[t].y2=y1+h,a[t].c=-z1;
        }
        sort(y+1,y+t+1);
        T=unique(y+1,y+t+1)-y-1;//去重
        build(1,1,T);
        sort(a+1,a+t+1,cmp);
        for(int i=1;i<=t;++i) {
            update(1,1,T,a[i]);
            ans=Max(ans,mx[1]);
        } 
        printf("%lld\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值