题意:平面上有n个点,每个点有一个value,每两个点之间都有一条线,这条线的value是两点的value之积。求过原点一条线,切割的线的value之和最大。
先对这n个点极角排序,然后假设这个直线是从原点指向第一个点的。那么左边的点的value之和记为s1,右边的记为s2,第一个点算在左边,加在s1里。(可以看做指向第一个点右边一点点)然后向左旋转,从这个点离开,就将这个点的值从s1中减去,加在s2中,并且要记录s1加上了多少,s2减去了多少,这里的加上减去就是这个线的尾巴扫过的点的value之和。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
#include <vector>
#include <queue>
#include <set>
#include <map>
using namespace std;
#define ll long long
const double pi=acos(-1.0);
const double eps=1e-8;
int T;
int n;
struct Point{
int x,y;
int val;
double ang;
}point[200000+50];
double angle(Point a){
double x=abs(a.x);
double y=abs(a.y);
if (a.x>0&&a.y>0) return atan(y/x)*180.0/pi;
else if (a.x<0&&a.y>0) return 180.0-atan(y/x)*180.0/pi;
else if (a.x<0&&a.y<0) return 180.0+atan(y/x)*180.0/pi;
else if (a.x>0&&a.y<0) return 360.0-atan(y/x)*180.0/pi;
else if (a.x==0){
if (a.y>0) return 90;
else if (a.y<0) return 270;
else return -1;
}
else if (a.y==0){
if (a.x>0) return 0;
else if (a.x<0) return 180;
else return -1;
}
return -1;
}
bool cmp(Point a,Point b){
return a.ang<b.ang;
}
int main(){
// freopen("1.txt","r",stdin);
scanf("%d",&T);
while (T--){
scanf("%d",&n);
ll sum=0;
for (int i=1;i<=n;i++){
scanf("%d %d %d",&point[i].x,&point[i].y,&point[i].val);
sum+=point[i].val;
point[i].ang=angle(point[i]);
}
sort(point+1,point+n+1,cmp);
for (int i=n+1;i<=n+n;i++){
point[i]=point[i-n];
point[i].ang+=360;
}
ll s1=0,s2=0;
int r;
for (int i=1;i<=n;i++){
if (point[i].ang-point[1].ang<180){
s1+=point[i].val;
}
else{
r=i;
break;
}
}
s2=sum-s1;
int l=1;
ll ans=0;
while (l<=n){
ans=max(ans,s1*s2);
s1-=point[l].val;
s2+=point[l].val;
l++;
while (r<=2*n&&point[r].ang-point[l].ang<180){
s1+=point[r].val;
s2-=point[r].val;
r++;
}
}
printf("%lld\n",ans);
}
}