HDU6127 Hard challenge[计算几何]

Hard challenge

  HDU - 6127
 


题意:

给T组数据,每组数据有一个n,代表n个点,接下来n行,每行给出一个x和y还有val,代表每个点的位置和值。两个点连起来的值等于两点的val相乘,不存在两点同时存在于一条与原点相连的线上。问从原点处划一条直线,不与这n个点相交(即点都不能存在于这条直线上),能经过最大的边和是多少。


题解:

实际上,用直线分开两边,得到的和就是等于该线的上方的权值和乘以该线的下方的权值和。

先对这n个点进行极角排序,然后将他们分开4个象限,保存所有点的权值和,根据象限,也保存该象限的前缀和,方便后面的计算。

然后开始从第一象限的点开始扫描到第四象限,每扫描到一个点,同象限在其前面(包括它自身)的权值加起来,用双指针,在其旋转180度的象限中找到一个斜率比该点要大的点,将其与该象限剩下的点的权值加起来,还有将其顺时针90度的象限的所有点的权值也加起来,因为这些地方都是在该线的左侧。

然后枚举出来的该线的答案就是等于  当前的权值*(sum-当前的权值)。将所有点按象限都扫一次,就可以得到最大值了。



#pragma comment(linker, "/STACK:102400000,102400000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<queue>
#include<stack>
#include<set>
#include<map>
#include<vector>
using namespace std;
typedef long long ll;
const int N=5e4+5;
ll sum[5][N];
vector<int>vc[5];
int nx[]={0,4,1,2,3};
int fan[]={0,3,4,1,2};
struct point
{
    int x,y,val;
    point (int xx=0,int yy=0,int v=0):x(xx),y(yy),val(v){}
}p[N];
ll cross(const point &p1,const point &p2,const point &q1,const point &q2)
{
    return (ll)(q2.y-q1.y)*(p2.x-p1.x)-(ll)(q2.x-q1.x)*(p2.y-p1.y);
}
bool cmp(const point &a,const point &b)
{
    if (!a.y && !b.y && (ll)a.x*b.x<=0)
        return a.x>b.x;
    if (!a.y && a.x>=0 && b.y)
        return 1;
    if (!b.y && b.x>=0 && a.y)
        return 0;
    if ((ll)b.y*a.y<=0)
        return a.y>b.y;
    point one;
    one.y=one.x=0;
    return cross(one,a,one,b)>0 || (!cross(one,a,one,b) && a.x<b.x);
}
bool check(const point &a,const point &b)
{
    point np(-a.x,-a.y);
    return !cmp(np,b);
}
void push(int id,int i,int val)
{
    vc[id].push_back(i);
    sum[id][vc[id].size()]=sum[id][vc[id].size()-1]+val;
}
int main()
{
	int T;
	scanf("%d",&T);
	while (T--)
	{
	    int n;
	    scanf("%d",&n);
	    for (int i=0 ; i<n ; ++i)
            scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].val);
        sort(p,p+n,cmp);
        for (int i=0 ; i<=4 ; ++i)
        {
            vc[i].clear();
            for (int j=0 ; j<=n ; ++j)
                sum[i][j]=0;
        }
        ll s=0;
        for (int i=0 ; i<n ; ++i)
        {
            if (p[i].x>=0 && p[i].y>=0)
                push(1,i,p[i].val);
            else if (p[i].x<0 && p[i].y>=0)
                push(2,i,p[i].val);
            else if (p[i].x<0 && p[i].y<0)
                push(3,i,p[i].val);
            else
                push(4,i,p[i].val);
            s+=p[i].val;
        }
        ll mx=0;
        for (int i=1 ; i<=4 ; ++i)
        {
            if (!vc[i].size())
                continue;
            for (int j=0,k=0 ; j<vc[i].size() ; ++j)
            {
                ll tmp=sum[nx[i]][vc[nx[i]].size()]+sum[i][j+1];
                while (k<vc[fan[i]].size() && check(p[vc[i][j]],p[vc[fan[i]][k]]))
                    ++k;
                tmp+=sum[fan[i]][vc[fan[i]].size()]-sum[fan[i]][k];
                mx=max(tmp*(s-tmp),mx);
            }
        }
        printf("%lld\n",mx);
	}
	return 0;
}
/*
10
4
-5 2 100
-2 -3 100
100000000 1000000000 10
100000000 999999999 9
2
1 1 1
1 -1 1
3
1 1 1
1 -1 10
-1 0 100
4
2 1 10
-4 -1 20
-3 -4 30
-2 -5 40
3
-1 2 10
3 -1 20
1 -5 30
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值