蓝书(算法竞赛进阶指南)刷题记录——POJ2482 Stars in Your Window(扫描线+线段树)

题目:POJ2482.
题目大意:给定一个平面上的 n n n个点 ( x i , y i ) (x_i,y_i) (xi,yi),价值为 c i c_i ci,现在要求用一个长为 w w w宽为 h h h的矩形覆盖的点价值和最大(边界上的不算).
x i , y i , w , h ∈ N + , 1 ≤ n ≤ 1 0 4 , 1 ≤ w , h ≤ 1 0 6 , 0 ≤ x i , y i &lt; 2 31 x_i,y_i,w,h\in N_+,1\leq n\leq 10^4,1\leq w,h\leq 10^6,0\leq x_i,y_i&lt; 2^{31} xi,yi,w,hN+,1n104,1w,h106,0xi,yi<231.

首先考虑由于坐标是整数,所以不能算上边界可以直接转化为长宽均减 1 1 1.

我们可以转化一下,把每个点转化成一个矩形,表示只要用于覆盖的矩形的右上角在这个点对应点矩形之中,这个点就会有贡献.很容易发现点 i i i对应的矩形左下角为 ( x i , y i ) (x_i,y_i) (xi,yi),右上角为 ( x i + w − 1 , y i + h − 1 ) (x_i+w-1,y_i+h-1) (xi+w1,yi+h1).

现在问题就变成了在一个平面上寻找一个点,使得包含这个点的矩形权值和最大.

然后我们就可以把 n n n个矩形变成 2 n 2n 2n条扫描线,并且用线段树维护就好了.

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn).

代码如下:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
  using namespace std;

#define Abigail inline void
typedef long long LL;
#define int unsigned int      //注意,这道题坐标大小加个10^6都能爆int

const int N=10000;

int n,w,h;
struct seg{
  int x,l,r,v;
  seg(int X=0,int L=0,int R=0,int V=0){x=X;l=L;r=R;v=V;}
  bool operator < (const seg &p)const{return x<p.x;}
}e[N*2+9];
int ord[N*2+9],ans;

int lower(int k){
  int l=1,r=n<<1,mid=l+r>>1;
  for (;l<r;mid=l+r>>1)
    k>ord[mid]?l=mid+1:r=mid;
  return l;
}

int upper(int k){
  int l=1,r=n<<1,mid=l+r>>1;
  for (;l<r;mid=l+r+1>>1)
    k<ord[mid]?r=mid-1:l=mid;
  return l;
}

struct tree{
  int max,tag;
}tr[N*8+9];

void Pushup(int k){tr[k].max=max(tr[k<<1].max,tr[k<<1|1].max);}
void Update_add(int k,int v){tr[k].max+=v;tr[k].tag+=v;}
void Pushdown(int k){Update_add(k<<1,tr[k].tag);Update_add(k<<1|1,tr[k].tag);tr[k].tag=0;}

void Change_add(int L,int R,int v,int l,int r,int k=1){
  if (L==l&&R==r) {Update_add(k,v);return;}
  int mid=l+r>>1;
  Pushdown(k);
  if (R<=mid) Change_add(L,R,v,l,mid,k<<1);
  else if (L>mid) Change_add(L,R,v,mid+1,r,k<<1|1);
    else Change_add(L,mid,v,l,mid,k<<1),Change_add(mid+1,R,v,mid+1,r,k<<1|1);
  Pushup(k);
}

Abigail into(){
  int x,y,c;
  for (int i=1;i<=n;++i){
  	scanf("%u%u%u",&x,&y,&c);
  	e[i*2-1]=seg(x,y,y+h-1,c);e[i*2]=seg(x+w,y,y+h-1,-c);      //减去的时候在x+w处减去更好处理 
  	ord[i*2-1]=y;ord[i*2]=y+h-1;
  }
}

Abigail work(){
  ans=0;
  sort(e+1,e+1+2*n);
  sort(ord+1,ord+1+2*n);
  for (int i=1;i<=n<<1;++i)
    e[i].l=lower(e[i].l),e[i].r=upper(e[i].r);      //注意取两边端点 
  for (int i=1;i<=n<<1;){
  	for (int now=e[i].x;i<=n<<1&&e[i].x==now;++i) Change_add(e[i].l,e[i].r,e[i].v,1,n<<1);
  	ans=max(ans,tr[1].max);
  }
}

Abigail outo(){
  printf("%u\n",ans);
}

#undef int

int main(){
  while (~scanf("%u%u%u",&n,&w,&h)){
    into();
    work();
    outo();
  }
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值