【BZOJ1568】[JSOI2008] Blue Mary开公司(卡精度线段树)

点此看题面

大致题意: 两种操作:新给你一条斜率大于(0)的直线;给定横坐标(整数),问已知直线中最大的(y)值。

卡精度

绝望的卡精度。。。

可能是我的做法和别人不太一样,看了下题解似乎都是李超线段树(不知道这是什么神仙东西),甚至还有一个暴力水过的,好像就我一个人写线段树上二分 emmm

在BZOJ上卡精度卡了一个晚上都没能过,第二天到机房接着卡,又花了一个半小时才过了。

结果到洛谷上又(WA)了,只有(70)分,但实在是不想调,只好扔在那里,假装我过了。

当然,或许并不是精度炸了,而是写挂了,如果真是这样,希望能帮我指出。

线段树上二分

考虑每条直线的答案区间,最后发现必然长成这个样子:

每条直线斜率递增,且必然对应一段区间的答案

然后考虑新加入一条直线,如果对答案有贡献,就应该是这个样子的:

对于这种情况,可以发现必然是超过一条斜率较小的直线,最终又被斜率较大的直线超越。

所以我们就可以线段树上二分出它能作为答案的左右区间。

应该是挺好理解的。

至于二分的细节可以详见代码注释。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define DB long double
using namespace std;
int n;DB k[N+5],b[N+5];
class SegmenTree
{
	private:
		#define PT CI l=0,CI r=n-1,CI rt=1
		#define LT l,mid,rt<<1
		#define RT mid+1,r,rt<<1|1
		#define PU(x) (O[x].L=O[x<<1].L,O[x].R=O[x<<1|1].R)
		#define PD(x) O[x].id&&(T(x<<1,O[x].id),T(x<<1|1,O[x].id),O[x].id=0)
		#define T(x,p) (O[x].id=O[x].L=O[x].R=p)
		#define f(p,x) (k[p]*(x)+b[p])
		struct node {int id,L,R;}O[N<<2];
		I int FL(CI p,PT)//找左端点
		{
			if(f(O[rt].L,l)<=f(p,l)&&f(O[rt].R,r)<=f(p,r)) return l;
			//如果左端点和右端点的值都较小,说明肯定这整段区间都被新直线碾压,直接返回左端点
			if(l==r||(f(O[rt].L,l)>f(p,l)&&k[O[rt].L]>=k[p])) return -1;
			//如果到了单点,或者左端点的值比新直线大,且斜率更大,说明新直线永远无法超越原答案,返回-1
			RI mid=l+r>>1;PD(rt);if(k[O[rt<<1|1].L]>k[p]) return FL(p,LT);
			//因为要保证斜率递减,所以若右区间斜率不满足限制就去搞左区间
			return f(O[rt<<1].R,mid)<=f(p,mid)?FL(p,LT):FL(p,RT);
			//尽可能走左区间
		}
		I int FR(CI p,PT)//找右端点,和上面大致一样,不再重复
		{
			if(f(O[rt].L,l)<=f(p,l)&&f(O[rt].R,r)<=f(p,r)) return r;
			if(l==r||(f(O[rt].L,l)>f(p,l)&&k[O[rt].L]>=k[p])) return -1;
			RI mid=l+r>>1;PD(rt);if(k[O[rt<<1].R]<k[p]) return FR(p,RT);
			return f(O[rt<<1|1].L,mid+1)<=f(p,mid+1)?FR(p,RT):FR(p,LT);
		}
		I void U(CI x,CI y,CI p,PT)//区间修改为新直线
		{
			if(x<=l&&r<=y) return (void)T(rt,p);RI mid=l+r>>1;PD(rt);
			x<=mid&&(U(x,y,p,LT),0),y>mid&&(U(x,y,p,RT),0),PU(rt);
		}
	public:
		I void U(CI p)//修改
		{
			RI l=k[O[1].L]<=k[p]?FL(p):0,r=k[O[1].R]>=k[p]?FR(p):n-1;//注意没有斜率更小/斜率更大的直线的边界
			~l&&~r&&l<=r&&(U(l,r,p),0);
		}
		I int Q(CI x,PT)//询问
		{
			if(l==r||O[rt].id) return O[rt].id;
			RI mid=l+r>>1;return x<=mid?Q(x,LT):Q(x,RT);
		}
}S;
int main()
{
	char s[10];DB x,y;for(RI i=(scanf("%d",&n),1),t=0,p,q;i<=n;++i)
	{
		if(scanf("%s",s),s[0]=='P') ++t,scanf("%Lf%Lf",&b[t],&k[t]),S.U(t);//修改
		else scanf("%d",&q),p=S.Q(q-1),printf("%d
",(int)floor((k[p]*(q-1)+b[p])/100));//询问
	}return 0;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ1568.html