Problem
There are n points on the plane, and the ith points has a value
Idea
在已知直線將座標系分割爲兩部分時,求任意左上部分點到右下部分點的線段價值和,可以等效地看作是
顯然由於通過過原點直線分割圖形,可以將所有點按照極角排序。通過枚舉點 i (x, y) ,假設直線恰好通過點 i ,二分獲取最大的在極角排序中小於點 i 關於原點對稱點 (-x, -y) (題目保證不會有任意兩點的連線過原點)。則直接可以求當前劃分所產生的價值和(當然,需要區分 i 點在左上或右下的情況,顯然 i 過直線是不會產生價值,即總價值縮水,微偏移直線的斜率即可最大化當前枚舉情況的最優。)
當然,不使用二分,利用雙指針的做法來獲取左上點與右下點的分界也可。
HINT: 由於此題的座標取值較大,可能出現中間過程爆 int 的可能,最好將座標 x , y 取 long long 。:joy: 沒注意在這個問題上 WA 了兩發。
Code
#include<bits/stdc++.h>
using namespace std;
const int N = 50000 + 10;
int T, n, pre[N];
struct point {
long long x, y, val;
} p[N], ori, np;
double cross(const point &p1, const point &p2, const point &q1, const point &q2) {
return (q2.y - q1.y)*(p2.x - p1.x) - (q2.x - q1.x)*(p2.y - p1.y);
}
bool cmp(const point &a, const point &b)
{
if (a.y == 0 && b.y == 0 && a.x*b.x <= 0)return a.x>b.x;
if (a.y == 0 && a.x >= 0 && b.y != 0)return true;
if (b.y == 0 && b.x >= 0 && a.y != 0)return false;
if (b.y*a.y <= 0)return a.y>b.y;
return cross(ori,a,ori,b) > 0 || (cross(ori,a,ori,b) == 0 && a.x < b.x);
}
bool jug(int idx, int mid) {
np.x = -p[idx].x; np.y = -p[idx].y;
if(cmp(np, p[mid]) == true) return false;
return true;
}
int bs(int idx, int l, int r, int ans) {
int mid;
while(l <= r) {
mid = (l+r) / 2;
if(jug(idx, mid)) {
l = mid + 1, ans = mid;
} else {
r = mid - 1;
}
}
return ans;
}
long long calc(long long part) {
return part * (pre[n] - part);
}
long long solve()
{
long long ans = 0;
for(int i=1, lft, rgt;i<=n;i++)
{
if(p[i].y < 0) {
lft = bs(i, 1, i-1, 0);
ans = max(ans, calc(pre[i] - pre[lft]));
ans = max(ans, calc(pre[i-1] - pre[lft]));
} else {
rgt = bs(i, i+1, n, i);
ans = max(ans, calc(pre[rgt] - pre[i]));
ans = max(ans, calc(pre[rgt] - pre[i-1]));
}
}
return ans;
}
int main()
{
ori.x = ori.y = 0;
scanf("%d", &T);
while(T-- && scanf("%d", &n)!=EOF)
{
for(int i=1;i<=n;i++)
scanf("%lld %lld %lld", &p[i].x, &p[i].y, &p[i].val);
sort(p+1, p+n+1, cmp);
for(int i=1;i<=n;i++) {
pre[i] = pre[i-1] + p[i].val;
}
printf("%lld\n", solve());
}
}