[ABC139F] Engines || [POI2018]Pionek
题目描述
给定 \(N\) 个向量,选出一些向量使得它们的和的模长最大。
正解
发现选出来的向量,在极角排序之后一定是一段连续的区间。
由于数据范围很小,我这里给出的是 \(O(n^2)\) 做法,但其实还有更加优秀的 \(O(n \log n)\),(复杂度瓶颈在排序上,找区间用 two-pointer )。
如何用 two-pointer ?
假如已经知道答案向量的方向,那么对它有贡献的 \(180^{\circ}\) 的半平面的向量都必须选。
枚举左端点点向量,右端点往右边移动即可。
\(\color{DeepSkyBlue} {Code :}\)
#include <bits/stdc++.h>
#define N 105
using namespace std;
int n;
struct vec {
long long x, y;
vec (long long tx = 0, long long ty = 0) { x = tx, y = ty; }
vec operator + (const vec &t) const { return vec(x + t.x, y + t.y); }
vec operator - (const vec &t) const { return vec(x - t.x, y - t.y); }
long long lenth() { return x * x + y * y; }
}a[2 * N];
bool cmp(const vec &lhs, const vec &rhs) {
return atan2(lhs.y, lhs.x) < atan2(rhs.y, rhs.x);
}
inline int read() {
int x = 0; bool neg = false; char ch = getchar();
while(!isdigit(ch)) (ch == '-') && (neg = true), ch = getchar();
while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
return neg ? -x : x;
}
int main() {
n = read();
for(int i = 1; i <= n; ++i)
a[i].x = read(), a[i].y = read();
sort(a + 1, a + n + 1, cmp);
for(int i = 1; i <= n; ++i)
a[n + i] = a[i];
vec cur;
long long ans = 0;
for(int l = 1; l <= n; ++l) {
cur = vec(0, 0);
for(int r = l; r < l + n; ++r) {
cur = cur + a[r];
ans = max(ans, cur.lenth());
}
}
printf("%.10lf\n", sqrt(ans));
return 0;
}