[武汉加油] bzoj 5099: [POI2018]Pionek 几何+双指针
几何+双指针
题目大意:现在有 \(n\) 个向量,请你选出来一些向量使它们的和的长度最大,输出最大值的平方。
假如我们已经知道了最终向量的方向,我们要想使长度最大,就需要将所有投影在最终向量正方向上的向量都加起来。
所以我们可以按角度枚举最终向量的方向,我们需要加起来的就是一段移动的区间,我们可以用双指针来维护加起来的向量。
因为最终向量的方向有可能是给出的向量,也有可能是向量之间间隔的方向,所以每次移动指针的时候都要更新一下答案.
因为开始给出的向量不一定有序,所以我们需要先将向量排序。
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long
using namespace std;
int n;
const double PI = acos(-1);
LL sx, sy, ans;
struct data
{
LL x, y; double ang;
friend bool operator <(const data & a,const data & b) {return a.ang < b.ang;}
}a[400010];
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)scanf("%lld%lld", &a[i].x, &a[i].y), a[i].ang = atan2(a[i].y,a[i].x);
sort(a + 1, a + 1 + n);
for(int i = 1; i <= n; ++ i)a[n + i].x = a[i].x, a[n + i].y = a[i].y, a[n + i].ang = a[i].ang + 2 * PI;
for(int l = 1, r = 1; l <= n; ++ l)
{
while(r < n + l && a[r].ang - a[l].ang < PI)sx += a[r].x, sy += a[r].y, ans = max(ans, sx * sx + sy * sy), ++ r;
sx -= a[l].x, sy -= a[l].y, ans = max(ans, sx * sx + sy * sy);
}
cout << ans;
return 0;
}