[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;
}
posted @ 2020-03-24 16:20  Lskkkno1  阅读(180)  评论(0编辑  收藏  举报