JSOI 2008 Blue Mary开公司 题解

题目传送门

题目大意: n n n 次操作,每次操作有两种:1、增加一条直线;2、询问 x = T x=T x=T 时,在所有直线上 y y y 值的最大值。

题解

经典的李超线段树做法在这里,这篇博客讲另外一种cdq分治的做法。

脑补一下不难发现,最后对答案可能有贡献的那些直线,即处于最上方的那些直线,一定是一个下凸包的形状,像这样:
在这里插入图片描述
假如按时间顺序加入的直线的斜率递增,那么就可以很方便地维护出这个凸包,但题目并不保证这一点。

具体来说,一条直线能对一个询问产生贡献,需要满足: t i < t j t_i<t_j ti<tj

而我们希望直线按斜率递增的顺序出现,即 k i < k j k_i<k_j ki<kj

诶,那不是正好可以用cdq分治来做吗?

做法也很简单,将操作以时间顺序排好然后分治。每次递归完左右区间后,将左区间内的直线按斜率递增排序,然后一条一条加入单调队列。接着就可以很容易地更新右区间内的询问了。

当然,可以将右区间内的询问也进行排序,那么求解会变得更加简单,排序可以在分治时直接归并排序排好,时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

但是由于浮点数运算次数较多所以跑的并不算快,在吸了氧后跑到了 938 m s 938ms 938ms

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define maxn 100010
#define eps 1e-8

int n;
struct par{
    
    int type;double k,b;int pos;}a[maxn],tmp[maxn];
struct line{
    
    double k,b;}q[maxn];
int st,ed;
double meet(line x,line y){
    
    return (y.b-x.b)/(x.k-y.k);}
int dcmp(double x){
    
    return x>eps?1:x<-eps?-1:0;}
void cdq(int l,int r){
    
    
	if(l==r)return;
	int mid=l+r>>1;cdq(l,mid);cdq(mid+1,r);
	//求解
	q[st=ed=1]=(line){
    
    0,0};
	for(int i=l;i<=mid;i++)if(!a[i].type){
    
    
		line now=(line){
    
    a[i].k,a[i].b};
		while(ed>0&&dcmp(q[ed].k-now.k)==0&&dcmp(q[ed].b-now.b)<0)ed--;//特判斜率相同的直线
		while(ed>1&&dcmp(meet(q[ed],q[ed-1])-meet(q[ed],now))>0)ed--;
		q[++ed]=now;
	}
	for(int i=mid+1;i<=r;i++)if(a[i].type){
    
    
		while(st<ed&&a[i].k-meet(q[st],q[st+1])>eps)st++;
		a[i].b=max(a[i].b,q[st].b+q[st].k*a[i].k);
	}
	//归并
	int x=l,y=mid+1,now=0;
	while(x<=mid&&y<=r){
    
    
		if(dcmp(a[x].k-a[y].k)<0)tmp[++now]=a[x++];
		else tmp[++now]=a[y++];
	}
	while(x<=mid)tmp[++now]=a[x++];
	while(y<=r)tmp[++now]=a[y++];
	for(int i=l;i<=r;i++)a[i]=tmp[i-l+1];
}
bool cmp(par x,par y){
    
    return x.pos<y.pos;}

int main()
{
    
    
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
    
    
		char s[10];scanf("%s",s);
		if(s[0]=='P')a[i].type=0,scanf("%lf %lf",&a[i].b,&a[i].k),a[i].b-=a[i].k;
		else a[i].type=1,scanf("%lf",&a[i].k);
		a[i].pos=i;
	}
	cdq(1,n);
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=n;i++)
	if(a[i].type)printf("%d\n",(int)a[i].b/100);
}

猜你喜欢

转载自blog.csdn.net/a_forever_dream/article/details/109173929