题意
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;
}