【BZOJ】3190: [JLOI2013]赛车-半平面交

传送门:bzoj3190


题解

半平面交板题。

写半平面需要记住下面两张图:
在这里插入图片描述
加入一条边时首尾都可能更新。
在这里插入图片描述
需要把末尾多余的删除。

p.s.需要特判只与半平面交一个点和重合的线。

好卡精度啊。


代码

#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#define db double
using namespace std;
const db eps=1e-10;
const int N=1e4+2;
int n,bs[N],tot=1,ans[N];

struct P{
	db x,y;
};

struct L{
	int k,slop;
	int tag;
	bool operator <(const L& u)const{
	  if(slop==u.slop) 
	    return k<u.k;
	  return slop<u.slop;
	} 
}t[N];

inline db inter(L a,L b)
{
	return (db)(b.k-a.k)/(a.slop-b.slop);
}

void work()
{
   for(int i=2;i<=n;i++){
   	 if(t[i].slop!=t[i-1].slop) tot++;
   	 else if(t[i].slop==t[i-1].slop && t[i].k==t[i-1].k) tot++;
   	 t[tot]=t[i];
   }n=tot;tot=1;
   bs[tot]=1;ans[1]=t[1].tag;
   for(int i=2;i<=n;i++){
    	while(tot&&inter(t[i],t[bs[tot]])<-eps) tot--;
    	while(tot>1&&(inter(t[bs[tot-1]],t[bs[tot]])>inter(t[i],t[bs[tot-1]])))
    		  tot--;
    	bs[++tot]=i;ans[tot]=t[i].tag;
    }
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&t[i].k); 
	for(int i=1;i<=n;i++) scanf("%d",&t[i].slop);
	for(int i=1;i<=n;i++) t[i].tag=i;
	sort(t+1,t+n+1);
	work();
	printf("%d\n",tot);                                                                            
	sort(ans+1,ans+tot+1);
	for(int i=1;i<=tot;i++){
	 	if(tot==i) printf("%d",ans[i]);
	 	else printf("%d ",ans[i]);
	 }
	 return 0;
}

这个WA了(大概是精度被卡了TAT

#include<bits/stdc++.h>
#define sqr(x) (x*x)
typedef long double db;
using namespace std;
const db eps=1e-10;
const int N=10010;

int n,m,stk[N],hd,tl,ans[N];

char cp;
inline int rd()
{
	int x=0,f=1;cp=getchar();
	for(;!isdigit(cp);cp=getchar()) if(cp=='-') f=-1;
	for(;isdigit(cp);cp=getchar()) x=x*10+(cp^48);
	return x*f; 
}

struct P{
   db x,y;
   P(db x_=0,db y_=0):x(x_),y(y_){};
   inline P operator +(const P&ky){return P(x+ky.x,y+ky.y);}
   inline P operator -(const P&ky){return P(x-ky.x,y-ky.y);}
   inline db operator ^(const P&ky){return x*ky.y-y*ky.x;}
   inline P operator *(const db&ky){return P(x*ky,y*ky);}
   inline P operator /(const db&ky){return P(x/ky,y/ky);}
   inline db sq(){return sqr(x)+sqr(y);}
   bool operator <(const P&ky)const{
   	 db a=atan2(y,x),b=atan2(ky.y,ky.x);
   	 return a!=b?a<b:x<ky.x;
   } 
}p[N];

struct Line{
	P st,ed,dir;int id;
	Line(){};
	Line(P st_,P ed_){st=st_;ed=ed_;dir=ed-st;}
}le[N];

inline int dcmp(db x){if(fabs(x)<eps) return 0;return x>0?1:-1;}
inline bool onlf(P a,P b,P c){return (dcmp((c-b)^(b-a))>-1);}

inline bool cmp(Line a,Line b)
{
	if(a.dir.y==0 && b.dir.y==0) return a.dir.x<b.dir.x;
	if((a.dir.y<=0 && b.dir.y<=0)||(a.dir.y>0 && b.dir.y>0)){
		int re=dcmp(a.dir^b.dir);
		return (!re)?onlf(b.st,a.st,b.ed):re>0; 
	}
	return a.dir.y<b.dir.y;
}

inline P inter(Line a,Line b)
{
	db sa=a.dir^b.dir,sb=b.dir^(a.st-b.st);
	return a.st+(a.dir*sb/sa);
}

inline void hpi() 
{
	int i;sort(le+1,le+n+1,cmp);
	for(i=1;i<=n;++i) 
	  if((!m)||(dcmp(le[i].dir^le[i-1].dir))||((!dcmp(le[i].dir.y-le[i-1].dir.y)) && (!dcmp(le[i].st.y-le[i-1].st.y)))) le[++m]=le[i];
	n=m;stk[(hd=tl=1)]=1;
	for(i=2;i<=n;++i){
		for(;hd<tl && (!onlf(le[i].st,p[tl-1],le[i].ed));--tl);
		for(;hd<tl && (!onlf(le[i].st,p[hd],le[i].ed));++hd);
	    stk[++tl]=i;
	    if(hd<tl) p[tl-1]=inter(le[i],le[stk[tl-1]]);
	}
	for(;hd<tl && (!onlf(le[stk[hd]].st,p[tl-1],le[stk[hd]].ed));--tl);
	for(;hd<tl && p[hd].x<-eps;++hd);
} 

int main(){
    int i;n=rd();
    for(i=1;i<=n;++i) le[i].st.y=rd();
    for(i=1;i<=n;++i)
        le[i].dir.x=1,le[i].id=i,le[i].dir.y=rd();
    for(i=1;i<=n;++i) le[i].ed=le[i].st+le[i].dir;
    hpi();printf("%d\n",tl-hd+1);
    for(i=hd;i<=tl;++i) ans[i]=le[stk[i]].id;
    sort(ans+hd,ans+tl+1);
    for(i=hd;i<=tl;++i) printf("%d ",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值