AcWing2957. 赛车

题意

https://www.acwing.com/problem/content/2960/

思路

每个赛车的方程y = vi * t + ki;

可知这是一个在x正半轴的(t >= 0)的半平面交 添加两条封口的L 对于每条方程维护左边区域 所有在core上的边/点都满足曾经不慢于others的条件 有重边要处理一下

代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>

using namespace std;
typedef long double LD;
const LD eps = 1e-18;
const LD pi = acos(-1.0);
const double Inf = 1.0 * 0x3f3f3f3f3f3f3f3f;
const int MaxN = 1e4 + 5;
#define PII pair<int,int>

int n;
set<PII> y;
vector<int> ans;

struct P{
	int k,v;
}c[MaxN];

struct Point{
	LD x,y;
	Point(LD x = 0,LD y = 0) : x(x),y(y) {}
}a[MaxN];
typedef Point Vector;
 
struct Line{
	Point s,e;
	int id;
	Line() {}
	Line(Point a,Point b){ s = a,e = b;}
}L[MaxN],que[MaxN];//双端队列
 
int dcmp(LD x){//equal ?= 0
	if(fabs(x) < eps) return 0;
	else return x < 0 ? -1:1;
}
 
//运算符重载
Vector operator + (Vector A, Vector B){ return Vector(A.x+B.x, A.y+B.y); }
Vector operator - (Vector A, Vector B){ return Vector(A.x-B.x, A.y-B.y); }
Vector operator * (Vector A, LD p){ return Vector(A.x*p, A.y*p); }
bool operator == (const Point &a, const Point &b){
	return dcmp(a.x-b.x) == 0 && dcmp(a.y-b.y) == 0;
}
 
LD Dot(Vector A, Vector B){ return A.x*B.x + A.y*B.y; }
LD Cross(Vector A, Vector B){ return A.x*B.y - A.y*B.x; }
 
//得到极角角度
LD getAngle(Vector a){
	return atan2(a.y,a.x);
}
LD getAngle(Line a){
	return atan2(a.e.y - a.s.y,a.e.x - a.s.x);
}
 
//极角排序+同极角最左边拍在最后->去重
bool cmp(Line a,Line b){
	Vector va = a.e - a.s,vb = b.e - b.s;
	LD A = getAngle(va),B = getAngle(vb);
	if(!dcmp(A - B)) return Cross(va,b.e - a.s) > 0;//右优先
	return A < B;
}
 
Point Ist_L(Line a,Line b){//给两直线求交点
	LD a1 = a.s.y - a.e.y,b1 = a.e.x - a.s.x,c1 = a.s.x * a.e.y - a.e.x * a.s.y;
	LD a2 = b.s.y - b.e.y,b2 = b.e.x - b.s.x,c2 = b.s.x * b.e.y - b.e.x * b.s.y;
	return Point((c1*b2 - c2*b1)/(a2*b1 - a1*b2), (a2*c1 - a1*c2)/(a1*b2 - a2*b1));
}
 
bool OnRight(Line a,Line b,Line c){//bc交点?在a的左边
	Point o = Ist_L(b,c);
	return Cross(a.e - a.s,o - a.s) < 0;//a.s是zhou o在右边
}
 
void HalfPlane(int n){//半平面交 判断多边形是否有核
	sort(L,L + n,cmp);
	int head = 0,tail = 0,cnt = 0;//双端队列
	for(int i = 0;i < n - 1; i++){
		//去重 极角相同右边优先排序 所以取最后一个(最左边)
		if(!dcmp(getAngle(L[i]) - getAngle(L[i + 1]))) continue;
		L[cnt++] = L[i];
	}
	L[cnt++] = L[n - 1];
 
	for(int i = 0;i < cnt;i++){
		//删去较陡边
		while(tail - head > 1 && OnRight(L[i],que[tail - 1],que[tail - 2])) tail--;
		while(tail - head > 1 && OnRight(L[i],que[head],que[head + 1])) head++;
		que[tail++] = L[i];
	}
	//判断首尾(环)连接处的影响
	while(tail - head > 1 && OnRight(que[head],que[tail - 1],que[tail - 2])) tail--;
	while(tail - head > 1 && OnRight(que[tail - 1],que[head],que[head + 1])) head++;
	for(int i = head;i < tail; i++){
		int cur = que[i].id;
		if(cur == -1) continue;
		y.insert(PII(c[cur].k,c[cur].v));
	}
	for(int i = 1;i <= n; i++){
		if(y.count(PII(c[i].k,c[i].v))) ans.push_back(i);
	}
	int len = ans.size();
	printf("%d\n",len);
	for(int i = 0;i < len; i++){
		printf("%d",ans[i]);
		if(i != len - 1) printf(" ");
	}
	printf("\n");
//	if(tail - head < 3) return false;
//	return true;
}
 
int main()
{
	scanf("%d",&n);
	for(int i = 1;i <= n;i++) scanf("%d",&c[i].k);
	for(int i = 1;i <= n;i++) scanf("%d",&c[i].v);
	int cnt = 0;
	for(int i = 1;i <= n; i++){
		L[cnt++] = {{0,(LD)c[i].k},{1,(LD)c[i].k + c[i].v}};
		L[cnt - 1].id = i;
	}
	L[cnt++] = {{Inf,Inf},{0,Inf}};
	L[cnt - 1].id = -1;
	L[cnt++] = {{0,Inf},{0,0}};
	L[cnt - 1].id = -1;
	HalfPlane(cnt);
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值