hdu 6127---Hard challenge(思维)

题目链接

Problem Description

There are n points on the plane, and the ith points has a value vali, and its coordinate is (xi,yi). It is guaranteed that no two points have the same coordinate, and no two points makes the line which passes them also passes the origin point. For every two points, there is a segment connecting them, and the segment has a value which equals the product of the values of the two points. Now HazelFan want to draw a line throgh the origin point but not through any given points, and he define the score is the sum of the values of all segments that the line crosses. Please tell him the maximum score.

Input

The first line contains a positive integer T(1≤T≤5), denoting the number of test cases.
For each test case:
The first line contains a positive integer n(1≤n≤5×104).
The next n lines, the ith line contains three integers xi,yi,vali(|xi|,|yi|≤109,1≤vali≤104).

Output

For each test case:
A single line contains a nonnegative integer, denoting the answer.

Sample Input

2

2

1 1 1

1 -1 1

3

1 1 1

1 -1 10

-1 0 100

Sample Output

1

1100

题意:有 n 个点,每个点有个权值,点与点之间可以连成线段,线段的权值就是两个端点的权值乘积。任意两个点与原点不可能在一条直线上,求一条直线穿过的线段的最大权值和?

思路:我们可以想到,有一条过原点的直线,那么直线穿过的线段都是由直线两侧的点互相连线组成的线段,进一步发现线段的权值和就是两侧的点权值和的乘积。有了前面的简化,我们可以对所有的点按照斜率进行排序,从最小斜率的点开始遍历计算,每次以过当前点的原点的直线为直线,那么我们需要计算两侧点权值和的乘积,那么复杂度是O(n*n)。我们可以优化:第一次以O(n) 遍历计算直线两侧的权值和,那么紧接着的下一次的直线划分的上下两侧只有上次的那个点不同,所以只需要O(1)的考虑上次直线上的那么点是应该加入上侧还是下侧。 所以这部分的做法是O(n) 的,但因为斜率排序是O(n*logn)的,所以整体的复杂度是

O(n*logn)的。

代码如下:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long LL;
const LL N=5e4+5;
const double INF=1e18;
struct Node{
    LL x,y;
    LL v;
    double f;
}a[N];

void cal(Node& t)
{
    LL x=t.x;
    LL y=t.y;
    if(x==0) t.f=INF;
    else{
        t.f=(double)y*1.0/(double)x;
    }
}
LL cmp(const Node s1,const Node s2)
{
    return s1.f<s2.f;
}
bool check(Node a,Node b)
{
    if(((a.x*b.y-a.y*b.x)*1.0/a.x)>=0.0)
        return true;
    return false;
}

int main()
{
    LL T; cin>>T;
    while(T--)
    {
        LL n; scanf("%lld",&n);
        LL tot=0;
        for(LL i=1;i<=n;i++)
        {
            scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].v);
            tot+=a[i].v;
            cal(a[i]);
        }
        if(n==1) { puts("0"); continue; }
        sort(a+1,a+n+1,cmp);
        LL ans=0,tmp1=0,tmp2=0;
        tmp1=a[1].v;
        for(LL i=2;i<=n;i++)
        {
           if(!check(a[1],a[i]))
            tmp1+=a[i].v;
        }
       tmp2=tot-tmp1;
       ans=max(tmp1*tmp2,ans);
       if(a[1].x<0) tmp1-=a[1].v;
       for(LL i=2;i<=n;i++)
       {
           if(a[i].x>=0) tmp1+=a[i].v;
           tmp2=tot-tmp1;
           ans=max(tmp1*tmp2,ans);
           if(a[i].x<0) tmp1-=a[i].v;
       }
       printf("%lld\n",ans);
    }
    return 0;
}
时间: 2024-12-24 08:43:06

hdu 6127---Hard challenge(思维)的相关文章

【极角排序+双指针线性扫】2017多校训练七 HDU 6127 Hard challenge

acm.hdu.edu.cn/showproblem.php?pid=6127 [题意] 给定平面直角坐标系中的n个点,这n个点每个点都有一个点权 这n个点两两可以连乘一条线段,定义每条线段的权值为线段两端点点权的乘积 现在要过原点作一条直线,要求这条直线不经过任意一个给定的点 在所有n个点两两连成的线段中,计算与这条直线有交点的线段的权值和 最大化这个权值和并输出 题目保证,给定的n个点不重合且任意两个点的连线不经过原点 [思路] 一条经过原点的直线把n个点分成两个半平面A,B 假设A中的点权

HDU 6127: Hard challenge

Hard challenge #include<bits/stdc++.h>typedef long long L;using namespace std;#define endl '\n'const int MAXN=5*1e4+5;struct Point{ int x,y,val;}p[MAXN];bool cmp(const Point &x,const Point &y){ if(x.x==0)return true; if(y.x==0)return false;

hdu 6127 : Hard challenge (2017 多校第七场 1008)(计算几何)

题目链接 题意:二维平面上有n个点(没有重叠,都不在原点,任意两点连线不过原点),每个点有一个权值,用一条过原点的直线把他们划分成两部分,使两部分的权值和的乘积最大.输出最大的乘积. 极角排序后,将原来(-pi,pi]区间的元素copy到(pi,3pi],用双指针维护一个角度差不超过pi的区间,记区间的权值和为sum1,用sum1*(sum-sum)更新ans #include<bits/stdc++.h> using namespace std; typedef long long LL;

HDU 6127 Hard challenge (极角扫描)

题意:给定 n 个点,和权值,他们两两相连,每条边的权值就是他们两个点权值的乘积,任意两点之间的直线不经过原点,让你从原点划一条直线,使得经过的直线的权值和最大. 析:直接进行极角扫描,从水平,然后旋转180度,就可以计算出一个最大值,因为题目说了任意直线不是经过原点的,所以就简单了很多,每次碰到的肯定是一个点,而不是多个点. 代码如下: #pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdi

hdu 4898 LCP+贪心思维

题意:将一个字符串切成k块,使得字典序最大的那块最小. ORZ  WJMZBMR,几行题解读了一天才懂. 快速比较两个子串的大小可以利用LCP(最长公共前缀),比较公共前缀的下一个字符的大小就够了. 利用这种思想,首先我们可以预处理所有子串的LCP(后缀数组+记录 O(2nlog(2n))+O(n*n),dp(O(4*n*n))) 然后将这些子串利用LCP按照字典序排序,开始二分答案. 二分的答案就是这K个块字典序的上限.假设以i作为起点,由于字典序上限已知,所以我们可以立刻求出i点最远能选到哪

hdu 6127

http://acm.hdu.edu.cn/showproblem.php?pid=6127 题意:有N个点,每一个点都有一个权值,每两点可以组成一条边,每一个边的权值为两点的权值相乘,然后求一条直线可以穿过权值和最多的边为多少 思路:枚举,枚举每一个点与原点连成的直线,把所有的点分成两半,它的权值和就是两边的总和相乘. 根据极角进行排序,排序完后可以把这些点分为两部分,a部分的x是大于0的值的和,b部分的x是小于0的值的和,依次遍历这个序列,如果当前序列的点是大于0的,那么这个点可以 从a移到

HDU 6038 Function(思维+寻找循环节)

http://acm.hdu.edu.cn/showproblem.php?pid=6038 题意:给出两个序列,一个是0~n-1的排列a,另一个是0~m-1的排列b,现在求满足的f的个数. 思路: 先看一下样例吧: 对于这组数来说,假如我们先指定了f(0)对应的在b中的值,那么根据第2个式子,就可以得出f(1),根据f(1)就又可以得出f(2),最后根据f(2)就可以检验f(0)的值是否正确. 这也就是说,对于a中的一个循环节,只要确定了其中一个数所映射的值,那么其它数就都被相应的确定了. 所

HDU 6170 Two strings 思维 DP

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=6170 题目描述: 两个字符串问是否匹配, '.'可以匹配任意字符, '*'可以使前一个数的出现次数成上一个自然数(0, 1, 2, 3........) 解题思路: DP, dp(i, j)表示A串匹配到j位, B串匹配到i位两个串是否匹配, 转移方程再代码里有, 参考Jaihk662的博客, 注释在代码中 代码: #include <iostream> #include <cstdio&

第十场 hdu 6172 Array Challenge(矩阵快速幂)

http://acm.hdu.edu.cn/showproblem.php?pid=6172 题目大意:按照给出的公式算出an 解题思路:an=4an-1+17an-2-12an-3,不要问我为什么,我也不知道(?_?) AC代码: 1 #include <iostream> 2 #include<bits/stdc++.h> 3 //if(~i)//当i不是-1时满足条件 4 using namespace std; 5 const int SMod=1e9+7; 6 struc

第八场 hdu 6143 Killer Names(思维题)

http://acm.hdu.edu.cn/showproblem.php?pid=6143 题目大意:有名和姓两种字符串,每种字符串长度都是n个字符,名和姓加起来的字符种类数最多是m个,问满足条件的字符串有多少种? 解题思路:我们能够枚举左边的字符种数是x,那么右边就有(m-x)n种排列方式.我们知道左边的排列方式数量是C(m,x)*f(x).其中f(x)=(xn-C(x,x-1)*f(x-1)-C(x,x-2)*f(x-2)--C(x,1)*f(1)).最后结果就是枚举每一个x,对C(m,x