bzoj 1568: [JSOI2008]Blue Mary开公司(超哥线段树)

1568: [JSOI2008]Blue Mary开公司

Time Limit: 15 Sec   Memory Limit: 162 MB
Submit: 739   Solved: 250
[ Submit][ Status][ Discuss]

Description

Input

第一行 :一个整数N ,表示方案和询问的总数。 接下来N行,每行开头一个单词“Query”或“Project”。 若单词为Query,则后接一个整数T,表示Blue Mary询问第T天的最大收益。 若单词为Project,则后接两个实数S,P,表示该种设计方案第一天的收益S,以及以后每天比上一天多出的收益P。

Output

对于每一个Query,输出一个整数,表示询问的答案,并精确到整百元(以百元为单位,例如:该天最大收益为210或290时,均应该输出2)。

Sample Input

10
Project 5.10200 0.65000
Project 2.76200 1.43000
Query 4
Query 2
Project 3.80200 1.17000
Query 2
Query 3
Query 1
Project 4.58200 0.91000
Project 5.36200 0.39000

Sample Output

0
0
0
0
0

HINT

约定: 1 <= N <= 100000 1 <= T <=50000 0 < P < 100,| S | <= 10^6 提示:本题读写数据量可能相当巨大,请选手注意选择高效的文件读写方式。

Source

[ Submit][ Status][ Discuss]

题解:超哥线段树。

这道题相当于是插入n条直线y=kx+b 

然后查询某x位置所有直线中的最大值。

我们需要用到标记永久化的思想。每次加入一条直线,先判断直线的斜率和本区间记录的直线的斜率,再判断区间中点哪条直线的答案大,如果是斜率大的直线答案大,因为斜率递增,所以在[mid+1,r]中斜率小的直线不会再产生贡献,那么我们把当前区间的答案更改为斜率大的直线,同时将斜率小的直线向[l,mid]中下方。如果是斜率小的直线的答案大,那么斜率大的直线只可能在[mid+1,r]中产生贡献,那么就把当前区间的答案更改为斜率较小的直线,并把斜率大的直线向[mid+1,r]下方。

查询答案的时候就把经过的区间所记录的直线该点的值都计算一下,取最大值就是答案。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define N 500003
using namespace std;
int n,m,tr[N*4];
double a[N*2],b[N*2];
int pd(int x,int y,int pos)
{
	return a[x]+(pos-1)*b[x]>a[y]+(pos-1)*b[y];
}
void change(int now,int l,int r,int x)
{
	if (l==r)  
	{
		if (pd(x,tr[now],l))
		  tr[now]=x;
		return;
	}
	int mid=(l+r)/2;
    if (b[x]>b[tr[now]])
	 if (pd(x,tr[now],mid))
	  change(now<<1,l,mid,tr[now]),tr[now]=x;
	 else change(now<<1|1,mid+1,r,x);
    if (b[x]<b[tr[now]])
	 if (pd(x,tr[now],mid))
      change(now<<1|1,mid+1,r,tr[now]),tr[now]=x;
     else change(now<<1,l,mid,x);
}
double getans(int k,int x)
{
	return a[k]+(x-1)*b[k];
}
double qjmax(int now,int l,int r,int x)
{
	if (l==r)  return getans(tr[now],x);
	int mid=(l+r)/2;
	double ans=getans(tr[now],x);
	if (x<=mid)  ans=max(ans,qjmax(now<<1,l,mid,x));
	else ans=max(ans,qjmax(now<<1|1,mid+1,r,x));
	return ans;
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("my.out","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		char s[20]; scanf("%s",s);
		if (s[0]=='P')
		 {
		 	m++;
		 	scanf("%lf%lf",&a[m],&b[m]);
		 	change(1,1,N,m);
		 }
		else
		{
			int x; scanf("%d",&x);
			double t=qjmax(1,1,N,x);
			int k=t; 
			printf("%d\n",k/100);
		}
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值