【JLOI 2013】赛车

【题目】

传送门

题目描述:

这里有一辆赛车比赛正在进行,赛场上一共有 n n n 辆车,分别称为 g 1 g_1 g1 g 2 g_2 g2 … … …… g n g_n gn。赛道是一条无限长的直线。最初, g i g_i gi 位于距离起跑线前进 k i k_i ki 的位置。比赛开始后,车辆 g i g_i gi 将会以 v i v_i vi 单位每秒的恒定速度行驶。在这个比赛过程中,如果一辆赛车曾经处于领跑位置的话(即没有其他的赛车跑在他的前面),这辆赛车最后就可以得奖,而且比赛过程中不用担心相撞的问题。现在给出所有赛车的起始位置和速度,你的任务就是算出那些赛车将会得奖。

输入格式:

第一行有一个正整数 n n n 表示赛车的个数。

接下来一行给出 n n n 个整数,按顺序给出 n n n 辆赛车的起始位置。

再接下来一行给出 n n n 个整数,按顺序给出 n n n 辆赛车的恒定速度。

输出格式:

输出包括两行,第一行为获奖的赛车个数。

第二行按从小到大的顺序输出获奖赛车的编号,编号之间用空格隔开,注意最后一个编号后面不要加空格。

样例数据:

输入
4
1 1 0 0
15 16 10 20

输出
3
1 2 4

备注:

【数据范围】

对于 20 % 20\% 20% 的数据, n ≤ 10 n\le10 n10
对于 50 % 50\% 50% 的数据, n ≤ 6000 n\le6000 n6000
对于 100 % 100\% 100% 的数据, n ≤ 10000 , 0 ≤ k i ≤ 1 0 9 , 0 ≤ v i ≤ 1 0 9 n\le10000, 0\le k_i\le10^9, 0\le v_i\le10^9 n10000,0ki109,0vi109


【分析】

让我们先按样例数据画一下 x − t x-t xt 图:
在这里插入图片描述
其实不难发现的是,我们要维护的东西就是一个半平面交。

由于我们只能在第一象限内做半平面交,可以不用双端队列,一个普通队列就行了。

注意一些细节:

  1. 如果两条线的 k , b k,b k,b 都相等(即重合),都保留。
  2. 如果两条线只有 k k k 相等,那只保留 b b b 值大的那个。
  3. 如果一条线 x x x k , b k,b k,b 都比另一条线 y y y 的大,那只保留 x x x

然后按照普通的半平面交做就可以了。


【代码】

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 10005
#define eps 1e-8
using namespace std;
int n,tot;
struct line
{
	int k,b,id;
}l[N],q[N];
bool comp2(const line &p,const line &q){return p.id<q.id;}
bool comp1(const line &p,const line &q){return (p.k!=q.k)?p.k<q.k:p.b>q.b;}
double inter(line x,line y){return 1.0*(y.b-x.b)/(x.k-y.k);}
bool check(line i,line j,line k){return inter(i,j)>inter(i,k);}
void solve()
{
	int i;
	sort(l+1,l+n+1,comp1);
	for(i=1;i<=n;++i)
	{
		if(l[i].k==l[i-1].k&&l[i].b<l[i-1].b)continue;
		while(tot>=1&&inter(q[tot],l[i])<-eps)  tot--;
		while(tot>=2&&check(q[tot],q[tot-1],l[i]))  tot--;
		q[++tot]=l[i];
	}
	printf("%d\n",tot);
	sort(q+1,q+tot+1,comp2);
	for(i=1;i<=tot;++i)
	  printf("%d ",q[i].id);
}
int main()
{
	int i;
	scanf("%d",&n);
	for(i=1;i<=n;++i)  l[i].id=i;
	for(i=1;i<=n;++i)  scanf("%d",&l[i].b);
	for(i=1;i<=n;++i)  scanf("%d",&l[i].k);
	solve();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值