pro:给定N个二维平面的关键点,保证两点连线不经过原点。现在让你安排一条经过原点,但是不经过关键点的直线,使得两边的和的乘积最大。
sol:由于连线不经过原点,所以我们极角排序即可。
具体:因为我们的直线只需要180°,所以我们用atan(y/x)来排序,atan的范围是(-pi/2,pi/2); 而不是atan2。 这样的话,我们需要特殊处理y轴上的点。 由于没有两个同时在y轴上,所以我们可以把他看成y轴旁边的,这样不影响结果。 然后就是旋转这个直线,分成点在头部或者尾部来讨论加入还是移除。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=200010; const double pi=acos(-1.0); struct point{ ll x,y,val; double angle; }a[maxn]; bool cmp(point w,point v){ return w.angle<v.angle; } int main() { int T,N; ll ans; ll x,y; scanf("%d",&T); while(T--){ scanf("%d",&N); ans=x=y=0; rep(i,1,N) { scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].val); if(a[i].x==0) a[i].angle=0.5*pi; else a[i].angle=atan(1.0*a[i].y/a[i].x); } sort(a+1,a+N+1,cmp); rep(i,1,N) if(a[i].x<0) x+=a[i].val; else y+=a[i].val; ans=x*y; rep(i,1,N){ if(a[i].angle<=0){ if(a[i].y<0||(a[i].x>0&&a[i].y==0)) x+=a[i].val,y-=a[i].val; else x-=a[i].val,y+=a[i].val; } else { if(a[i].y>0) x+=a[i].val,y-=a[i].val; else x-=a[i].val,y+=a[i].val; } ans=max(ans,x*y); } printf("%lld ",ans); } return 0; }