李超树——由一次NOIP模拟赛引出的车祸记
李超树是什么?
李超树是一种用线段树来维护直线的一种方法,因为是集训队队员李超在论文中提到,所以尊称为李超树。
这是一次怎样的NOIP模拟赛?
这是一次关于生死的考验.
所以我十分认真对待。
第一题如此:
我一下子就想到了李超树,只是把直线换成抛物线而已嘛~
所以就开始着手。。
由于第一次打李超树,有点尴尬。
1个小时调对样例..
3个小时拍完...
然后很尴尬的发现极限数据我跑了23秒!!!
心态没了...
不想打比赛了...
4000+的程序,也许只有暴力分...
很尴尬我发现它跑到了90分...
代码如下
(博主很懒,没有打朴素的李超树)
代码很丑,情况很多
心态很崩
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#define LL long long
using namespace std;
const LL maxn=5*1e5+10;
const LL maxq=5*1e5+10;
const LL maxX=32323+10;
const LL XXX=-32324-100;
struct root{double x,x1;};
struct Moon{
LL a,b;
}f[2*maxX*4];
LL a[maxn],b[maxn];
LL x[maxq];
LL minx,maxx;
LL N,Q;
inline LL read(){
LL d=0,f=1;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
return d*f;
}
root cro(LL a1,LL b1,LL a2,LL b2,LL liml,LL limr){
LL a=a1-a2,b=b1-b2;
root R;
if(a==0){
R.x=0;
R.x1=XXX;
if(R.x>limr||R.x<liml) R.x=XXX;
}else{
LL delta=b*b;
if(delta==0){
R.x=(-b+sqrt(delta+0.0)+0.0)/(2*a+0.0);
R.x1=XXX;
if(R.x>limr||R.x<liml) R.x=XXX;
}else{
double R1=(-b+sqrt(delta+0.0)+0.0)/(2*a+0.0);
double R2=(-b-sqrt(delta+0.0)+0.0)/(2*a+0.0);
if(R1>R2) swap(R1,R2);
R.x=R1,R.x1=R2;
while(R.x!=XXX&&(R.x>=limr||R.x<=liml))
R.x=R.x1,R.x1=XXX;
}
}
return R;
}
LL val(LL a,LL b,LL x){
return a*x*x+b*x;
}
void add(LL v,LL l,LL r,LL a,LL b,LL L,LL R){
if(f[v].a<XXX){
f[v].a=a,f[v].b=b;
return;
}
if(f[v].a==a&&f[v].b==b) return;
root p=cro(a,b,f[v].a,f[v].b,l,r);
if(p.x==XXX){
int ll=l;
while(ll<r&&val(a,b,ll)==val(f[v].a,f[v].b,ll)) ++ll;
if(val(a,b,ll)>val(f[v].a,f[v].b,ll)) f[v].a=a,f[v].b=b;
return;
}else{
LL nl,nr;
if(p.x1==XXX){
//交点只有一个
int ll=l;
while(ll<r&&val(a,b,ll)==val(f[v].a,f[v].b,ll)) ++ll;
if(val(a,b,ll)>val(f[v].a,f[v].b,ll))
nl=l,nr=floor(p.x);
else nl=ceil(p.x),nr=r;
ll=r;
while(ll>l&&val(a,b,ll)==val(f[v].a,f[v].b,ll)) --ll;
if(val(a,b,ll)>val(f[v].a,f[v].b,ll)&&nl==l&&nr==floor(p.x)){
f[v].a=a,f[v].b=b;
return;
}
if(val(a,b,ll)<val(f[v].a,f[v].b,ll)&&nl==ceil(p.x)&&nr==r) return;
int mid=((l+r)%2!=0)?(l+r-1)/2:(l+r)/2;
if(nr<=mid) add(v*2,l,mid,a,b,nl,nr);
else if(nl>mid) add(v*2+1,mid+1,r,a,b,nl,nr);
else{
add(v*2,l,mid,a,b,nl,mid);
add(v*2+1,mid+1,r,a,b,mid+1,nr);
}
}else{
bool flag=0;
nl=ceil(p.x),nr=floor(p.x1);
int ll=nl;
while(ll<nr&&val(a,b,ll)==val(f[v].a,f[v].b,ll)) ++ll;
if(val(a,b,ll)==val(f[v].a,f[v].b,ll)){
ll=nl;
while(ll>l&&val(a,b,ll)==val(f[v].a,f[v].b,ll)) --ll;
if(val(a,b,ll)==val(f[v].a,f[v].b,ll)){
ll=nr;
while(ll<r&&val(a,b,ll)==val(f[v].a,f[v].b,ll)) ++ll;
}
if(val(a,b,ll)>val(f[v].a,f[v].b,ll)) flag=1;
}else if(val(a,b,ll)<val(f[v].a,f[v].b,ll)) flag=1;
if(flag){
// two side
int nl1,nr1;
nl1=l,nr1=nl-1;
int mid=((l+r)%2!=0)?(l+r-1)/2:(l+r)/2;
if(nr1<=mid) add(v*2,l,mid,a,b,nl1,nr1);
else if(nl1>mid) add(v*2+1,mid+1,r,a,b,nl1,nr1);
else{
add(v*2,l,mid,a,b,nl1,mid);
add(v*2+1,mid+1,r,a,b,mid+1,nr1);
}
nl1=nr+1,nr1=r;
mid=((l+r)%2!=0)?(l+r-1)/2:(l+r)/2;
if(nr1<=mid) add(v*2,l,mid,a,b,nl1,nr1);
else if(nl1>mid) add(v*2+1,mid+1,r,a,b,nl1,nr1);
else{
add(v*2,l,mid,a,b,nl1,mid);
add(v*2+1,mid+1,r,a,b,mid+1,nr1);
}
}else{
//one side
int mid=((l+r)%2!=0)?(l+r-1)/2:(l+r)/2;
if(nr<=mid) add(v*2,l,mid,a,b,nl,nr);
else if(nl>mid) add(v*2+1,mid+1,r,a,b,nl,nr);
else{
add(v*2,l,mid,a,b,nl,mid);
add(v*2+1,mid+1,r,a,b,mid+1,nr);
}
}
}
}
if(l==r) return;
}
LL query(LL v,LL l,LL r,LL x,LL s){
if(f[v].a>XXX)s=max(s,val(f[v].a,f[v].b,x));
if(l==r) return s;
int mid=((l+r)%2!=0)?(l+r-1)/2:(l+r)/2;
if(x<=mid) return query(v*2,l,mid,x,s);
else return query(v*2+1,mid+1,r,x,s);
}
int main(){
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
N=read();
Q=read();
LL i,j;
for (i=1;i<=N;++i)
a[i]=read(),b[i]=read();
memset(f,128,sizeof(f));
minx=1e18;
maxx=-1e18;
for (i=1;i<=Q;++i){
x[i]=read();
minx=min(x[i],minx);
maxx=max(x[i],maxx);
}
for (i=1;i<=N;++i)
add(1,minx,maxx,a[i],b[i],minx,maxx);
for (i=1;i<=Q;++i)
printf("%lld\n",query(1,minx,maxx,x[i],-1e18));
}