2017 Multi-University Training Contest - Team 7:1008. Hard challenge(模拟)

其他题目题解:

2017 Multi-University Training Contest - Team 7:1005. Euler theorem

2017 Multi-University Training Contest - Team 7:1011. Kolakoski

2017 Multi-University Training Contest - Team 7:1003. Color the chessboard

2017 Multi-University Training Contest - Team 7:1002. Build a tree

2017 Multi-University Training Contest - Team 7:1010. Just do it




一开始很少人做这道,估计都被1010卡住了,实际是道水题

题意:坐标系中有n个点,每个点都有它的价值,除此之外任意两点之间都有连线,连线的价值是两个点的价值乘积,现在你可以画一条经过原点的直线,你可以获得这条直线穿过的所有线段的价值和,求最大价值


题解:所有点按斜率排序,注意不是无脑从小到大,因为斜率的变化范围是(0,) (-∞,0),所以要稍微处理一下

之后考虑一条与x轴重合的直线,所有在一二象限的点和在x轴正半轴上的点都在直线上面,其他都在直线下面

记录valup为所有上方点的价值和,valdown为所有下方点的价值和,

这样可以O(n)计算这条直线获得的价值(∑val[i]*valdown (i∈up))


然后就可以慢慢的顺时针旋转这条直线,直到翻转180°,中间每经过一个点就说明当前点跑到了直线的另外一边,重新计算一次价值和,最后中间的最大价值就是答案

但这复杂度不是O(n²)的么,遍历O(n),每次计算O(n)

其实不然,因为每次只有一个点发生变化,所以可以O(1)重新计算答案

假设遍历到x点时,x从直线的上端到了直线下端

那么显然有sum -= val[i]*valdown;valdown += val[i];valup -= val[i];sum += valup

这样就只有排序的复杂度了


#include<stdio.h>
#include<algorithm>
using namespace std;
#define LL long long
typedef struct Point
{
	double x, y;
	double k;
	LL t, val;
	bool operator < (const Point &b) const
	{
		if(k>0 && b.k<0)
			return 1;
		else if(k<0 && b.k>0)
			return 0;
		else
			return k<b.k;
	}
}Point;
Point s[50005];
int main(void)
{
	LL T, n, i, up, down, now, ans;
	scanf("%lld", &T);
	while(T--)
	{
		scanf("%lld", &n);
		up = down = 0;
		for(i=1;i<=n;i++)
		{
			scanf("%lf%lf%lld", &s[i].x, &s[i].y, &s[i].val);
			if(s[i].y>=0)
			{
				if(s[i].x==0)
					s[i].k = 1e9+1000;
				else
					s[i].k = s[i].y/s[i].x;
				if(s[i].y==0 && s[i].x<0)
				{
					down += s[i].val;
					s[i].t = -1;
				}
				else
				{
					up += s[i].val;
					s[i].t = 1;
				}
			}
			else
			{
				if(s[i].x==0)
					s[i].k = 1e9+1000;
				else
					s[i].k = s[i].y/s[i].x;
				down += s[i].val;
				s[i].t = -1;
			}
		}
		now = 0;
		for(i=1;i<=n;i++)
		{
			if(s[i].t==1)
				now += s[i].val*down;
		}
		ans = now;
		sort(s+1, s+n+1);
		for(i=1;i<=n;i++)
		{
			if(s[i].t==1)
			{
				s[i].t = -1;
				now -= s[i].val*down;
				up -= s[i].val;
				down += s[i].val;
				now += s[i].val*up;
			}
			else
			{
				s[i].t = 1;
				now -= s[i].val*up;
				up += s[i].val;
				down -= s[i].val;
				now += s[i].val*down;
			}
			ans = max(ans, now);
		}
		printf("%lld\n", ans);
	}
	return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值