NYOJ 2361-流星雨(线段树)


题解:

对于二维坐标系,两种操作

• 1. 插入一条射线,以x=1为起点

• 2. 查询横坐标为X (整数)时所有射线y值的最大值

• 1 ≤ n, q ≤ 1e5

•新加入一条射线可能会影响若干区间

•即最后区间[a,b]的答案可能由”折线”组成

•这也正是本题难点

•考虑标记永久化。即对于线段树上的每个点 (代表一个区间)除非由严格更优的射线出现,否则不删除此线段 (新线段直接下传)

•按照斜率及区间中点处取值分四种情况讨论• 画图说明

•如何计算答案?(注意题目是单点询问)

•答案即为X在线段树中对应的叶子到根路径上所有的点答案取max

• O(n logn)

#include<set>  
#include<map>         
#include<stack>                
#include<queue>                
#include<vector>        
#include<string>      
#include<math.h>       
#include<stdio.h>                
#include<iostream>                
#include<string.h>                
#include<stdlib.h>        
#include<algorithm>       
#include<functional>        
using namespace std;
typedef long long ll;
#define inf 1000000000           
#define mod 1000000007                 
#define maxn  500005     
#define PI 3.1415926  
#define lowbit(x) (x&-x)     
#define eps 1e-9   
bool flag[maxn * 4];
double a[maxn * 4], b[maxn * 4], ans;
double work(double x, double y, double xx, double yy)
{
	return (yy - y) / (x - xx);
}
void insert(int id, int l, int r, double bb, double k)
{
	if (!flag[id])
		a[id] = k, b[id] = bb, flag[id] = 1;
	else
	{
		double t1 = 1.0*k*l + bb, t2 = 1.0*k*r + bb;
		double tt1 = 1.0*a[id] * l + b[id], tt2 = 1.0*a[id] * r + b[id];
		if (t1 <= tt1 && t2 <= tt2)
			return;
		else if (t1 > tt1 && t2 > tt2)
			a[id] = k, b[id] = bb;
		else
		{
			int mid = (l + r) / 2;
			double y = work(k, bb, a[id], b[id]);
			if (t1 >= tt1)
			{
				if (y <= mid)
					insert(id * 2, l, mid, bb, k);
				else
					insert(id * 2 + 1, mid + 1, r, b[id], a[id]), a[id] = k, b[id] = bb;
			}
			else
			{
				if (y > mid)
					insert(id * 2 + 1, mid + 1, r, bb, k);
				else
					insert(id * 2, l, mid, b[id], a[id]), a[id] = k, b[id] = bb;
			}
		}
	}
}
void query(int id, int l, int r, int x)
{
	if (flag[id])
		ans = max(ans, a[id] * (double)x + b[id]);
	if (l == r) return;
	int mid = (l + r) / 2;
	if (x <= mid)
		query(id * 2, l, mid, x);
	else
		query(id * 2 + 1, mid + 1, r, x);
}
int main(void)
{
	double x, y;
	int q, t, mx = -1;
	scanf("%d", &q);
	while (q--)
	{
		scanf("%d", &t);
		if (t == 1)
		{
			scanf("%lf%lf", &x, &y);
			insert(1, 1, maxn - 5, x - y, y);
		}
		else
		{
			int xx;
			scanf("%d", &xx);ans = 0;query(1, 1, maxn - 5, xx);
			printf("%lld\n", (ll)floor(ans / 100.0));
		}
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值