hdu 5091 Beam Cannon 离散化+扫描线+线段树

Beam Cannon

Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)

Total Submission(s): 551    Accepted Submission(s): 207

Problem Description

Recently, the γ galaxies broke out Star Wars. Each planet is warring for resources. In the Star Wars, Planet X is under attack by other planets. Now, a large wave of enemy spaceships is approaching. There is a very large Beam Cannon on the Planet X, and it
is very powerful, which can destroy all the spaceships in its attack range in a second. However, it takes a long time to fill the energy of the Beam Cannon after each shot. So, you should make sure each shot can destroy the enemy spaceships as many as possible.

To simplify the problem, the Beam Cannon can shot at any area in the space, and the attack area is rectangular. The rectangle parallels to the coordinate axes and cannot rotate. It can only move horizontally or vertically. The enemy spaceship in the space can
be considered as a point projected to the attack plane. If the point is in the rectangular attack area of the Beam Cannon(including border), the spaceship will be destroyed.

Input

Input contains multiple test cases. Each test case contains three integers N(1<=N<=10000, the number of enemy spaceships), W(1<=W<=40000, the width of the Beam Cannon’s attack area), H(1<=H<=40000, the height of the Beam Cannon’s attack area) in the first line,
and then N lines follow. Each line contains two integers x,y (-20000<=x,y<=20000, the coordinates of an enemy spaceship).

A test case starting with a negative integer terminates the input and this test case should not to be processed.

Output

Output the maximum number of enemy spaceships the Beam Cannon can destroy in a single shot for each case.

Sample Input

2 3 4
0 1
1 0
3 1 1
-1 0
0 1
1 0
-1

Sample Output

2
2

链接:http://acm.hdu.edu.cn/showproblem.php?pid=5091

题意:有n个点,给你w*h的框框,问你最多可以框住几个点,边缘也算。

做法:把每个点x-w/2,y-h/2,  与x+w/2,y+h /2,作一个矩形,可以知道,只有那个框框的中心在这个矩形中就可以覆盖这个点。然后就把所有点的矩形画出来,计算最大重合的层数就行了。实际操作中  可以把每个矩形看作  左下角为 x,y,右上角为x+w,y+h。 也就相当于一起平移。最大重合层数不变。

这题和我之前做得算面积的线段树不同。因为这里关注的不在是面积,所以也就不再关注宽度了。所以这里 线段树里的每个点0-(k-1)代表的不是一段长度的状态了,而是每一个点的状态。

所以本来的 r=Bin(s[i].r,k,x)-1;这一句 由原模版变成了r=Bin(s[i].r,k,x)。

然后就是基本功,把线段树改成算最大值的了。cover记录每个区间最大值。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<set>
#define ll __int64
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const ll maxn=50010;

struct Seg
{
	ll l,r,h;
	ll flag;
	Seg(){}
	Seg(ll a,ll b,ll c,ll d):l(a),r(b),h(c),flag(d){}
	bool operator<(const Seg &hh) const
	{
		return h<hh.h;
	}
};

struct Seg s[maxn<<3];
ll cover[maxn<<2],add[maxn<<2];
ll x[maxn<<2];
ll ans;
void PushUp(ll rt,ll l,ll r)
{
	cover[rt]=max(cover[rt<<1],cover[rt<<1|1])+add[rt];
}

void Update(ll L,ll R,ll f,ll l,ll r,ll rt)
{
	if(L<=l && r<=R)
	{
		cover[rt]+=f;//这个点的覆盖
		add[rt]+=f;
		return ;
	}
	ll m=(l+r)>>1;
	if(R<=m) Update(L,R,f,lson);
	else if(L>m) Update(L,R,f,rson);
	else
	{
		Update(L,R,f,lson);
		Update(L,R,f,rson);
	}
	PushUp(rt,l,r);
}

ll Bin(ll k,ll n,ll x[])
{
	ll l,r,m;
	l=0,r=n-1;
	while(l<=r)
	{
		m=(l+r)>>1;
		if(x[m]==k) return m;
		else if(x[m]>k) r=m-1;
		else l=m+1;
	}
	return -1;
}
set<ll> ss;
set<ll>::iterator it;
int main()
{
	ll n;
	ll i;
	ll ww,hh;

	ll xx,yy;
	while(scanf("%I64d",&n),n!=-1)
	{

		scanf("%I64d%I64d",&ww,&hh);
		ll m=0;
		ss.clear();
		for(i=0;i<n;i++)
		{
			scanf("%I64d %I64d",&xx,&yy);

			ss.insert(xx+ww);
			ss.insert(xx);

			s[m++]=Seg(xx,xx+ww,yy,1);
			s[m++]=Seg(xx,xx+ww,yy+hh,-1);
		}
		sort(s,s+m);
		ll k=0;
		for(it=ss.begin();it!=ss.end();it++)
			x[k++]=*it;

		memset(cover,0,sizeof cover);
		memset(add,0,sizeof add);

		ans=0;
		for(i=0;i<m-1;i++)
		{
			ll l=Bin(s[i].l,k,x);
			ll r=Bin(s[i].r,k,x);
			if(l<=r) Update(l,r,s[i].flag,0,k-1,1);
			ans=max(ans,cover[1]);
		}
		printf("%I64d\n",ans);
	}
	return 0;

} 
时间: 2024-10-13 16:20:02

hdu 5091 Beam Cannon 离散化+扫描线+线段树的相关文章

hdu 5091 Beam Cannon(线段树扫描线)

题目链接:hdu 5091 Beam Cannon 题目大意:给定N个点,现在要有一个W?H的矩形,问说最多能圈住多少个点. 解题思路:线段的扫描线,假设有点(x,y),那么(x,y)~(x+W,y+H)形成的矩形,以框的右下角落的位置是可以圈住(x,y) 点,所以N个点即为N个矩形,求覆盖的最大次数,扫描线裸题. #include <cstdio> #include <cstring> #include <vector> #include <algorithm&

poj 1151 Atlantis (离散化 + 扫描线 + 线段树)

题目链接题意:给定n个矩形,求面积并,分别给矩形左上角的坐标和右上角的坐标. 分析: 1 #include <iostream> 2 #include <cstdio> 3 #include <vector> 4 #include <cstring> 5 #include <cstdlib> 6 #include <algorithm> 7 #define LL __int64 8 #define lson l, mid, 2*rt

hdu 5091 Beam Cannon(线段树+扫描线+离散化)

Beam Cannon Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 457    Accepted Submission(s): 175 Problem Description Recently, the γ galaxies broke out Star Wars. Each planet is warring for resou

hdu 5091 Beam Cannon

题目大意: 有n个点(n<=10000),点的坐标绝对值不超过20000,然后问你用一个w*h(1<=w,h<=40000)的矩形,矩形的边平行于坐标轴,最多能盖住多少个点. 刘汝佳黑书上有原题 下面这份代码是加了离散化的,用垂直于x轴的直线去扫描,在y轴上建立线段树,所以对于每一个点(x,y),赋予权值1,,然后增加一个新的负点(x+w,y),赋予权值-1.当扫描线扫过每一个点,用该点的权值去区间更新线段树.维护每个区间的sum值,因为是区间更新,所以还要打个lazy标记. 1 #in

线段树+扫描线 HDOJ 5091 Beam Cannon

题目传送门 1 /* 2 题意:给出若干个点的坐标,用一个矩形去覆盖,问最多能覆盖几个点 3 线段树+扫描线:思路是先建一棵以[y, y + h]的树,左右儿子[x, x + w] 4 以这棵树为范围,从左到右扫描,更新点数,x的+1, x+w的-1(超过矩形范围) 5 ans = 每次更新时所覆盖点数的最大值 6 */ 7 #include <cstdio> 8 #include <algorithm> 9 #include <iostream> 10 #includ

HDU 1255 覆盖的面积 (扫描线 线段树 离散化)

题目链接 题意:中文题意. 分析:纯手敲,与上一道题目很相似,但是刚开始我以为只是把cnt>=0改成cnt>=2就行了,. 但是后来发现当当前加入的线段的范围之前 还有线段的时候就不行了,因为虽然现在都不等于 2,但是之前的那个线段加上现在的已经覆盖2次了. 1 #include <iostream> 2 #include <cstdio> 3 #include <vector> 4 #include <cstring> 5 #include &

HNU12884_Area Coverage(扫描线/线段树+离散化)

解题报告 题目传送门 题意: 又是求面积并 思路: 又是求面积并,还被坑了,题目明明描述的是int坐标,用了double才过... #include <algorithm> #include <iostream> #include <cstring> #include <cstdio> using namespace std; struct Seg { double lx,rx,h; int v; friend bool operator <(Seg

HDU 3642 - Get The Treasury - [加强版扫描线+线段树]

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3642 Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Problem Description Jack knows that there is a great underground treasury in a secret region. And he has a special d

HDU 1394 Minimum Inversion Number(线段树求逆序对)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1394 解题报告:给出一个序列,求出这个序列的逆序数,然后依次将第一个数移动到最后一位,求在这个过程中,逆序数最小的序列的逆序数是多少? 这题有一个好处是输入的序列保证是0 到 n-1,所以不许要离散化,还有一个好处就是在计算在这个序列中比每个数大和小的数一共有多少个的时候可以在O(1)时间计算出来,一开始我没有意识到,还傻傻的用了两层for循环来每次都计算,当然这样果断TLE了.把一个数从第一个移