题目大意:

多组测试数据(t<=5),每组给你平面直角坐标系上的 n 个(1<=n<=5e4)。这n个点两两之间有一条连线,连线值为这两点的值的乘积。现在让你选一条过原点的直线,使得经过直线的线段的值的和最大。

分析:

直线绕原点旋转,尺取每个点和原点的连线和 x 轴正方向的夹角(0~2π)。即:尺取每个点进入直线上区域或下区域。

未AC代码:

#include<bits/stdc++.h>
#define maxn 50050
using namespace std;
int n;
double small=1e-7;
double p=4*atan(1.0);
struct point
{
    int x;int y;
    double ang;
    long long int v;
}a[maxn];

double Get_ang(int x,int y)
{
    double t=(double)(y)/(double)(x);
    if(x>0&&y>0)return atan(t);
    if(x<0&&y>0)return p-atan(-t);
    if(x<0&&y<0)return p+atan(t);
    if(x>0&&y<0)return 2*p-atan(-t);
    if(y==0)
    {
        if(x>0)return 0;
        else return p;
    }
    if(x==0)
    {
        if(y>0)return p/2;
        else return 3*p/2;
    }
}

bool cmp(point a,point b)
{
    return a.ang<b.ang;
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        long long int up=0,down=0,temp_v;
        int num=0,temp_x,temp_y;
        for(int i=0;i<n;i++)
        {
            scanf("%d%d%lld",&temp_x,&temp_y,&temp_v);
            if(temp_x==0&&temp_y==0)continue;
            a[num].x=temp_x;
            a[num].y=temp_y;
            a[num].v=temp_v;
            a[num].ang=Get_ang(a[num].x,a[num].y);
            if(a[num].y>0||(a[num].y==0&&a[num].x>0))//x轴正方向上得点在up,x轴负方向上的点在down。
            {
                up+=a[i].v;
            }
            else
            {
                down+=a[i].v;
            }
            num++;
        }
        //printf("%lld %lld\n",up,down);
        sort(a,a+num,cmp);
        long long int ans=down*up;
        n=num;
        int j=0;
        for(int i=0;i<n;i++)
        {
            if(a[i].ang>p-small)
            {
                j=i;
                break;
            }
        }
        int i=0;
        while(1)//每次循环计算第i个在下面,第j个在上面的值
        {
            if(i>=n||abs(a[i].ang-p)<small)break;
            up-=a[i].v;down+=a[i].v;
            while(1)
            {
                if(i>=n||a[i+1].ang-a[i].ang>small)break;
                i++;
                up-=a[i].v;down+=a[i].v;
            }
            while(1)
            {
                if(j>=n||a[j].ang>a[i].ang+p+small)break;
                up+=a[j].v;down-=a[j].v;
                j++;
            }
            j--;
            ans=max(ans,up*down);
        }

         while(1)//每次循环计算第i个在下面,第j个在上面的值.2
        {
            if(i>=n)break;
            up-=a[i].v;down+=a[i].v;
            while(1)
            {
                if(i>=n||a[i+1].ang-a[i].ang>small)break;
                i++;
                up-=a[i].v;down+=a[i].v;
            }
            if(i>=n)i=n-1;
            j=max(j,i+1);
            while(1)
            {
                if(j>=n||a[j].ang>a[i].ang-p+small&&j<i)break;
                up+=a[j].v;down-=a[j].v;
                j++;
                j%=n;
            }
            j--;
            ans=max(ans,up*down);
        }



       /* for(int i=0;i<n-1;i++)
        {
            up-=a[i].v;
            down+=a[i].v;
            if(a[i+1].ang>p-small)
            {
                j%=n;
                while(1)
                {
                    if(a[j].ang>a[i+1].ang-p-small)break;
                    up+=a[j].v;
                    down-=a[j].v;
                    j++;
                    j%=n;
                }
            }
            else while(1)
            {
                if(a[j].ang>p+a[i+1].ang-small)break;
                up+=a[j].v;
                down-=a[j].v;
                j++;
                j%=n;
            }
            printf("%lld %lld\n",up,down);
            ans=max(ans,up*down);
            //printf("%lld",ans);
        }*/
        printf("%lld\n",ans);
    }

}

边界问题。