1. 程式人生 > >【BZOJ5099】[POI2018]Pionek 幾何+雙指針

【BZOJ5099】[POI2018]Pionek 幾何+雙指針

tput www int 極角 ios name main turn rip

【BZOJ5099】[POI2018]Pionek

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

HINT

技術分享圖片

題解:假如我們已經確定了最終向量的方向,那麽我們就會選擇所有在這個方向上投影為正的向量。於是我們將所有向量按極角排序,然後枚舉所有方向,用前綴和維護向量的和。可以先將序列倍長,然後用雙指針法掃一遍即可。

不過需要註意的是,我們枚舉的方向不僅是所有向量的方向,還有所有向量之間間隔的方向,所以我們在每個指針移動的時候都更新一下答案即可。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=200010;
int n;
ll ans;
struct node
{
	int x,y;
	double a;
}p[maxn<<1];
ll sx[maxn<<1],sy[maxn<<1];
bool cmp(const node &a,const node &b)
{
	return a.a<b.a;
}
inline void check(int l,int r)
{
	if(l<=r)	ans=max(ans,(sx[r]-sx[l-1])*(sx[r]-sx[l-1])+(sy[r]-sy[l-1])*(sy[r]-sy[l-1]));
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+(gc^‘0‘),gc=getchar();
	return ret*f;
}
int main()
{
	n=rd();
	int i,j;
	for(i=1;i<=n;i++)	p[i].x=rd(),p[i].y=rd(),p[i].a=atan2(p[i].x,p[i].y),p[i+n]=p[i],p[i+n].a+=2*pi;
	sort(p+1,p+2*n+1,cmp);
	for(i=1;i<=2*n;i++)	sx[i]=sx[i-1]+p[i].x,sy[i]=sy[i-1]+p[i].y;
	for(i=j=1;i<=2*n;i++)
	{
		for(;j<i&&p[j].a<=p[i].a-pi;j++,check(j,i-1));
		check(j,i);
	}
	for(;j<=2*n;check(j,2*n),j++);
	printf("%lld",ans);
	return 0;
}//2 1 10 1 -10

【BZOJ5099】[POI2018]Pionek 幾何+雙指針