画一条经过原点的线,图上每个点都有权值,让线两边的每个点的值相乘,让总和最大
要让总和最大,要让线两边的值的差距尽量小
先计算出每个点与x轴的夹角大小,用atan2 , rad是与x轴的夹角,取值范围为-π到π
用于排序
然后从第一个点划一条经过原点的线,用isleft这个函数判断出所有在这条线右边的点,将这些点的权值相加,计算差距
然后到第二个点划一条经过原点的线,这时可以发现,这条线与上条线右边点数的变化就是少了第二个点,然后再加上右边多出来的点
这样遍历 等于就是那条线 转一圈就行了
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
struct node
{
ll x,y,val;
double rad;
bool operator <(const node &rhs)const
{
return rad<rhs.rad;
}
}feng[50005];
bool isleft(node a,node b)//根据斜率判断一个点是否在一条线的左边
{
return a.x*b.y >= a.y*b.x;
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0);
int t,n;
cin>>t;
while(t--)
{
cin>>n;
ll sum=0;
for(int i=0;i<n;i++)
{
cin>>feng[i].x>>feng[i].y>>feng[i].val;
feng[i].rad= atan2(feng[i].y,feng[i].x);//rad是与x轴的夹角,取值范围为-π到π
sum+=feng[i].val;
}
sort(feng,feng+n);
int l=0,r=1;
ll sumr=0;
ll temp=sum;
while(l<n)
{
while( isleft(feng[l],feng[r]) )
{
if(l==r)
break;
sumr+=feng[r].val;
r=(r+1)%n;
}
temp=min(temp,abs(sum-2*sumr));
l++;
sumr-=feng[l].val;
}
cout<<( (sum-temp)/2 )*( (sum-temp)/2+temp )<<endl;
}
}