[BZOJ 1007][HNOI2008]水平可见直线

Description

Input

第一行为N(0 < N < 50000),接下来的N行输入Ai,Bi

Output

从小到大输出可见直线的编号,两两中间用空格隔开,最后一个数字后面也必须有个空格

Sample Input

3
-1 0
1 0
0 0

Sample Output

1 2

HINT

Source

这个题其实和计算几何没太大关系,需要用单调栈维护可以被看见的直线数,需注意有一个性质:若直线i没有被完全覆盖,则直线i与i-1的交点在i与i+1交点的左侧,可以自己用笔画一画来验证,为了防止被坑,先用unique将重复的直线清掉,每次清掉单调栈中被完全覆盖的直线,再放入新的直线,所有直线经过此操作后,再用ans数组记下所有可以被看见的直线的编号,sort排序得到升序的直线编号,就解出答案了

#include <stdio.h>
#include <cmath>
#include <algorithm>
#define MAXN 50050
using namespace std;
double ESP=0.000000000000001; //正无穷小,判断double数是否相等
int n,ans[MAXN],top=1; //ans数组保存按直线编号升序输出的答案,栈顶为top-1
struct Line
{
	int A,B,id; //第id条直线,直线解析式为y=ax+b
	bool operator==(const Line &b)const { return A == b.A;}
    bool operator<(const Line &b)const { return A == b.A ? ( B > b.B ) : A < b.A ; }
	void read(int x) //输入直线
	{
		id=x;
		scanf("%d%d",&A,&B);
	}
};
Line ln[MAXN],stack[MAXN]; //数组ln[]保存直线,数组stack[]保存单调栈
double getLineItsX(Line l1,Line l2) //求两直线交点横坐标(由一元方程推得)
{
	return (double)(l2.B-l1.B)/(double)(l1.A-l2.A);
}
void init()
{
	int i,j;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
		ln[i].read(i); //读取直线
}
void work()
{
	//性质:若直线i有露出部分,则直线i与i-1的交点在i与i+1交点的左侧
	int i,j; 
	sort(&ln[1],&ln[n+1]);
	n=unique(&ln[1],&ln[n+1])-&ln[1]; //现在,n=不重复的直线数
	for(i=1;i<=n;i++) //将直线一条一条地放入坐标系中,放入一条,就删掉被覆盖看不见的直线
	{
		while(top>2&&getLineItsX(stack[top-1],stack[top-2])>getLineItsX(stack[top-1],ln[i])-ESP)
			top--; //当直线top-1被直线top和top-2覆盖掉时,直线top出栈
		stack[top++]=ln[i]; //所有覆盖的直线都删光以后,加入第i条直线
	}
	for(i=1;i<top;i++)
		ans[i]=stack[i].id;
	sort(ans+1,ans+top); //对结果按id号升序排序
}
void output()
{
	int i,j;
	for(i=1;i<top;i++)
		printf("%d ",ans[i]);
	printf("\n");
}
int main()
{
	init();
	work();
	output();
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值