Codevs1273:风战 【计算几何+dp】

3 篇文章 0 订阅
2 篇文章 0 订阅

传送门


SOL

注意看清楚题面: y ≥ 0 y\ge0 y0

可以以点为单位转移,也可以像我这样以边为单位转移。
按边的极角排序,用vector d[i]存下以i点为终点的所有线段,每次枚举线段用dp转移即可。
d p [ i ] = m a x ( d p [ j ] ) + v a l [ i ] dp[i]=max(dp[j])+val[i] dp[i]=max(dp[j])+val[i]

以边为单位,代码丑。。

可以以点为单位,预处理原点与另外两点的三角形的代价,每次枚举前两个点转移,代码50行不到。。

时间复杂度:O(n^3)

可以用平衡树维护极角序列,第三维实现 O{log)查询;
复杂度:O(n^2log)


CODE

这个代码丑。。找个时间补一个平衡树O(n^2logn)算法;

#include<bits/stdc++.h>
using namespace std;
#define sf scanf
#define pf printf
#define ll long long
#define db double
#define cs const
cs int N=2e4+10,lowf=-2139062144;
cs db eps=1e-8,pi2=acos(-1)*2,pi=acos(-1);
inline bool equal(cs db &a,cs db &b){return abs(a-b)<eps;}
inline bool eles(cs db &a,cs db &b){return equal(a,b)|a<b;}
struct Vector{
	ll x,y;
	Vector (ll x=0,ll y=0):x(x),y(y){}
	friend Vector operator +(cs Vector &a,cs Vector &b){return Vector(a.x+b.x,a.y+b.y);}
	friend Vector operator -(cs Vector &a,cs Vector &b){return Vector(a.x-b.x,a.y-b.y);}
	friend ll operator ^(cs Vector &a,cs Vector &b){return a.x*b.y-a.y*b.x;}
	friend ll operator *(cs Vector &a,cs Vector &b){return a.x*b.x+a.y*b.y;}
	friend bool operator ==(cs Vector &a,cs Vector &b){return a.x==b.x&&a.y==b.y;}
}p[N],ch[N];
struct Line{
	Vector st,ed;
	int id,id2;
	db ang;
	bool operator <(cs Line &t)cs{
		return ang<t.ang;
	}
};
int n,m,cnt=0,dp[N];
Line l[N];
vector <int> d[N];
inline db lenth(cs Vector &a){
	return sqrt(a.x*a.x+a.y*a.y);
}
inline bool check(cs Vector &p0,cs Vector &a,cs Vector &b,cs Vector &c){
	db a1=acos(((a-p0)*(b-p0))/(lenth(a-p0)*lenth(b-p0)));
	db a2=acos(((b-p0)*(c-p0))/(lenth(b-p0)*lenth(c-p0)));
	db a3=acos(((c-p0)*(a-p0))/(lenth(a-p0)*lenth(c-p0)));
	if(equal(a1+a2+a3,pi2))return 1;
	return 0;
}
inline int calc(cs int &k){
	int ans=0;
	for(int i=1;i<=n;++i)if(check(p[i],Vector(0,0),l[k].st,l[k].ed))++ans;
	for(int i=1;i<=m;++i)if(check(ch[i],Vector(0,0),l[k].st,l[k].ed))--ans;
	return ans;
}
int ans=0;
signed main(){
	memset(dp,128,sizeof dp);
	sf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)sf("%lld%lld",&p[i].x,&p[i].y);
	for(int i=1;i<=m;++i)sf("%lld%lld",&ch[i].x,&ch[i].y);
	p[0].x=p[0].y=0;
	for(int i=0;i<=n;++i){
		for(int j=0;j<=n;++j){
			if(i^j){
				db ang;
				if(p[i].y^p[j].y){
					ang=atan2(p[j].y-p[i].y,p[j].x-p[i].x);
					if(eles(ang,0))ang+=pi2;	
				}
				else {
					ang=p[i].x<p[j].x? 0 : pi;
				}
				l[++cnt]=(Line){p[i],p[j],i,j,ang};	
			}
		}
	}
	sort(l+1,l+cnt+1);
	for(int i=1;i<=cnt;++i)d[l[i].id2].push_back(i);
	for(int i=1;i<=cnt;++i){
		if(l[i].st==Vector(0,0)){
			dp[i]=1;
			continue;
		}
		int up=d[l[i].id].size(),d2=lowf;
		for(int j=0;j<up;++j){
			if(dp[d[l[i].id][j]]==lowf)continue;
			d2=max(d2,dp[d[l[i].id][j]]);
		}
		if(d2==lowf)continue;
		int d1=calc(i);
		dp[i]=d1+d2+1;
		if(l[i].ed==p[0])ans=max(ans,dp[i]-1);
		
	}
	cout<<ans;
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值