[POJ2482]Stars in Your Window(离散化+扫描线+线段树)

70 篇文章 0 订阅
13 篇文章 0 订阅

题目描述

传送门

题解

这道题最主要的idea在于一个转化思想,原题是有若干带权点,求一个可能的矩形,可以转化成有若干个带权矩形,权可以累加,求一个最大的交。
做法就是,对于每一个点,预处理一个范围,这个范围表示当窗的右上角在这个范围内时可以覆盖当前点。很显然这个范围是一个矩形,记这个矩形的权为点权。那么我们就得到了n个带权矩形。求最大的交可以用扫描线来实现,将纵坐标离散化横坐标排序,然后从左往右扫,遇到矩形的左边界就在当前矩形的纵坐标范围内加上矩形的权,遇到右边界就减去,每次操作的全局最大值即为可能的最大交。维护用线段树实现。
当n<=100的时候 O(n3) 暴力就有60分辣= =不过在考场上忘清ans了然后就全挂了= =讨来一顿打

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 10005
#define LL long long

int n,yy,cnt,lsh,now,ans;LL w,h;
struct hp{int x,z;LL y;}star[N];
struct hq{int l,r,val;LL x;}squ[N*2];
int p[N*2],loc[N*2],maxn[N*8],delta[N*8];
LL Y[N*2];

inline void clear()
{
    ans=0; yy=0; cnt=0; lsh=0; memset(maxn,0,sizeof(maxn)); memset(delta,0,sizeof(delta));
    memset(p,0,sizeof(p)); memset(Y,0,sizeof(Y)); memset(loc,0,sizeof(loc));
}
inline int cmp(int x,int y)
{
    return Y[x]<Y[y];
}
inline int cmps(hq a,hq b)
{
    return a.x<b.x||(a.x==b.x&&a.val>b.val);
}
inline void update(int now)
{
    maxn[now]=max(maxn[now<<1],maxn[now<<1|1]);
}
inline void pushdown(int now,int l,int r,int mid)
{
    if (delta[now])
    {
        maxn[now<<1]+=delta[now]; delta[now<<1]+=delta[now];
        maxn[now<<1|1]+=delta[now]; delta[now<<1|1]+=delta[now];
        delta[now]=0;
    }
}
inline void interval_change(int now,int l,int r,int lrange,int rrange,int v)
{
    int mid=(l+r)>>1;
    if (lrange<=l&&r<=rrange)
    {
        maxn[now]+=v;
        delta[now]+=v;
        return;
    }
    pushdown(now,l,r,mid);
    if (lrange<=mid) interval_change(now<<1,l,mid,lrange,rrange,v);
    if (mid+1<=rrange) interval_change(now<<1|1,mid+1,r,lrange,rrange,v);
    update(now);
}
inline int query(int now,int l,int r,int lrange,int rrange)
{
    int mid=(l+r)>>1,ans=0;
    if (lrange<=l&&r<=rrange) return maxn[now];
    pushdown(now,l,r,mid);
    if (lrange<=mid) ans=max(ans,query(now<<1,l,mid,lrange,rrange));
    if (mid+1<=rrange) ans=max(ans,query(now<<1|1,mid+1,r,lrange,rrange));
    return ans;
}
int main()
{
    while (~scanf("%d%lld%lld",&n,&w,&h))
    {
        clear();
        for (int i=1;i<=n;++i) scanf("%lld%lld%d",&star[i].x,&star[i].y,&star[i].z);
        for (int i=1;i<=n;++i)
        {
            squ[++cnt].x=star[i].x; squ[cnt].val=star[i].z;
            squ[++cnt].x=star[i].x+w-1; squ[cnt].val=-star[i].z;
            Y[++yy]=star[i].y,Y[++yy]=star[i].y+h-1;
        } 
        for (int i=1;i<=yy;++i) p[i]=i;
        sort(p+1,p+yy+1,cmp);
        for (int i=1;i<=yy;++i)
            if (Y[p[i]]!=Y[p[i-1]]) loc[p[i]]=++lsh;
            else loc[p[i]]=lsh;
        cnt=0; yy=0;
        for (int i=1;i<=n;++i)
        {
            yy+=2;
            squ[++cnt].l=loc[yy-1]; squ[cnt].r=loc[yy];
            squ[++cnt].l=loc[yy-1]; squ[cnt].r=loc[yy]; 
        }
        sort(squ+1,squ+cnt+1,cmps);
        for (int i=1;i<=cnt;++i)
        {
            interval_change(1,1,lsh,squ[i].l,squ[i].r,squ[i].val);
            now=query(1,1,lsh,1,lsh);
            if (now>ans) ans=now;
        }
        printf("%d\n",ans);
    }
}

总结

写错的地方:
1、2^31-1是int的范围,需要用到long long,数据范围别再搞错了。
2、每一个矩形会贡献两个边,数组不要开小了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值