bzoj5099 [POI2018]Pionek 极角排序

Description


在无限大的二维平面的原点(0,0)放置着一个棋子。你有n条可用的移动指令,每条指令可以用一个二维整数向量表
示。每条指令最多只能执行一次,但你可以随意更改它们的执行顺序。棋子可以重复经过同一个点,两条指令的方
向向量也可能相同。你的目标是让棋子最终离原点的欧几里得距离最远,请问这个最远距离是多少?

第一行包含一个正整数n(n<=200000),表示指令条数。
接下来n行,每行两个整数x,y(|x|,|y|<=10000),表示你可以从(a,b)移动到(a+x,b+y)。

Solution


假如我们知道了最终和向量的方向,那么答案就是选取向量在这个方向上投影的长度之和
由此可以得出一个结论,我们一定只会从某个向量开始顺时针连续选取一段向量,并且最大的夹角不超过180°
于是乎极角排序然后两个指针扫就没了

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

typedef long long LL;
const double pi=acos(-1);
const double eps=1e-13;
const int N=400005;

struct pos {
	LL x,y; double arc;
	LL operator *(pos b) const {
		return x*b.y-y*b.x;
	}
	pos operator +(pos b) const {
		return (pos) {x+b.x,y+b.y};
	}
	pos operator -(pos b) const {
		return (pos) {x-b.x,y-b.y};
	}
	LL len() {
		return x*x+y*y;
	}
} p[N];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

bool cmp(pos a,pos b) {
	return a.arc<b.arc;
}

int main(void) {
	freopen("data.in","r",stdin);
	freopen("myp.out","w",stdout);
	int n=read(),cnt=0;
	rep(i,1,n) {
		int x=read(),y=read();
		if (1LL*x*x+1LL*y*y) {
			p[++cnt]=(pos) {x,y,atan2(x,y)};
		}
	} n=cnt;
	std:: sort(p+1,p+n+1,cmp);
	rep(i,1,n) p[i+n]=p[i],p[i+n].arc=p[i].arc+2*pi;
	pos ans=(pos) {0,0};
	LL prt=0;
	for (int l=1,r=1;l<=n;) {
		while (r<l+n&&(p[r].arc-p[l].arc<=pi+eps)) {
			ans=ans+p[r++];
			prt=std:: max(prt,ans.len());
		}
		ans=ans-p[l++];
		prt=std:: max(prt,ans.len());
	}
	printf("%lld\n", prt);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值