HDU 6127 Hard challenge
题意
平面坐标系,给出n个点,保证任意两点的连线不过原点.
每一个点都有一个权值,两点之间的线段的权值等于端点权值之积
问如果过原点做一条直线,直线穿过的线段的权值和最大是多少
解决
这道题我们卡住了,想到了要用极角排序来做,也想到了去枚举每一个经过原点和点的直线,但是思路卡在了”如何表示偏差一点点”上面
//直线左侧的点一定能和直线右侧的点连成线段,那么直线两端任意两点权值积的和,就是两个区间权值和的积
//好像有点绕╮(╯▽╰)╭...
/*
p1 /
/ p3 p1(p3+p4)+p2(p3+p4)=(p1+p2)*(p3+p4)
p2 /
/ p4 手动绘图 ∩_∩)O哈哈哈~
/
*/
- 容易知道,直线两端任意两点权值积的和,就是两个区间权值和的积(代码里面画了图(想不到吧^_^))
- 首先按照极角对每个点进行排序
- 初始直线从y轴开始,把x>=0的点都加到右区间,x<0的点都加到左区间
- 直线逆时针旋转,每旋转到一个点,把这个点从它原来所在的区间里面剔除掉,加到另一个去见里面
- 保存最大值
#define rep(i,a,b) for(int i=a;i<(b);++i)
struct point
{
int x,y,val;
double angle;
}P[50005];
bool cmp(point p1,point p2)
{
return p1.angle<p2.angle;
}
int main()
{
int n,cases;
long long ans,lsum,rsum; //lsum和rsum不会爆int但是相乘会爆int
scanf("%d",&cases);
while(cases--)
{
scanf("%d",&n);
rep(i,0,n){
scanf("%d%d%d",&P[i].x,&P[i].y,&P[i].val);
if(P[i].x==0) //这里对x=0进行特判,除数不能为0
{
if(P[i].y>0) P[i].angle=PI/2.0;
else P[i].angle=-PI/2.0;
}
else P[i].angle=atan(P[i].y*1.0/P[i].x);
}
//rep(i,0,n) cout<<P[i].angle<<endl;
sort(P,P+n,cmp);
ans=1;
lsum=rsum=0;
//初始先以y轴对做表面进行划分
rep(i,0,n){
if(P[i].x>=0) rsum+=P[i].val; //出现在y轴右侧(包含y轴)的点全部添加到右区间
else lsum+=P[i].val;
}
//直线左侧的点一定能和直线右侧的点连成线段,那么直线两端任意两点权值积的和,就是两个区间权值和的积
//好像有点绕╮(╯▽╰)╭...
/*
p1 /
/ p3 p1(p3+p4)+p2(p3+p4)=(p1+p2)*(p3+p4)
p2 /
/ p4 手动绘图 ∩_∩)O哈哈哈~
/
*/
ans=lsum*rsum;
rep(i,0,n){ //根据极角排序的顺序,遍历每一个点,然后把这个点从它之前所在的区间里剔除加到另一个区间
if(P[i].x>=0) //原来在右区间,从右区间剔除
{
rsum-=P[i].val;
lsum+=P[i].val;
}
else //原来在左区间,从左区间剔除
{
rsum+=P[i].val;
lsum-=P[i].val;
}
ans=max(ans,lsum*rsum);
}
cout<<ans<<endl;
}
}