[bzoj5099][几何]Pionek

3 篇文章 0 订阅

Description

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

Input

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

Output

输出一行一个整数,即最大距离的平方。

Sample Input

5

2 -2

-2 -2

0 2

3 1

-3 1

Sample Output

26

题解

高端啊…
首先你要知道
向量和的长度等于这些向量在最终向量下投影的长度和
把向量按极角排序
对于一个向量 与他垂直的180度平面上的向量对他都做正贡献
发现这个平面一定会与某个向量重合 或者在两个相邻的向量之间
枚举每个向量作为平面 双指针扫到这个180度平面的最后一个向量
可以计算出第一种情况
对于第二种情况 其实就相当于去掉了最后一个向量
你把最后一个向量暴力去掉就行了…

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#define LL long long
#define mp(x,y) make_pair(x,y)
using namespace std;
const double pi=acos(-1.0);
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline void write(int x)
{
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
inline void print(int x){write(x);printf(" ");}
struct node{int x,y;double ang;}w[410000];int n;
bool cmp(node n1,node n2){return n1.ang<n2.ang;}
LL sqr(LL x){return x*x;}
int main()
{
	n=read();
	for(int i=1;i<=n;i++)w[i].x=read(),w[i].y=read(),w[i].ang=atan2(w[i].y,w[i].x);
	sort(w+1,w+1+n,cmp);
	for(int i=1;i<=n;i++)w[i+n]=w[i],w[i+n].ang=w[i].ang+2*pi;
	LL s1=w[1].x,s2=w[1].y,ans=0;
	for(int i=1,p=1;i<=n;i++)
	{
		s1-=w[i-1].x;s2-=w[i-1].y;ans=max(ans,sqr(s1)+sqr(s2));
		while(p<i+n&&w[p+1].ang-w[i].ang<pi)p++,s1+=w[p].x,s2+=w[p].y,ans=max(ans,sqr(s1)+sqr(s2));
		ans=max(ans,sqr(s1-w[p].x)+sqr(s2-w[p].y));
	}
	printf("%lld\n",ans);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值