| ||||||||||
| Online Judge | Problem Set | Authors | Online Contests | User | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Web Board Home Page F.A.Qs Statistical Charts | Current Contest Past Contests Scheduled Contests Award Contest | |||||||||
雁过留声——动态半平面交算法看似二次函数
f(x)=a*x*x+b*x+c
因为b=a*2,其实是一次函数
g((x+1)^2)=a*((x+1)^2)-a+c
于是,题目转化为:给一堆直线g(x)=a*x+d,求某个点的最小值
换句话说,就是给一堆半平面,求交的下边界
两个序列:
A[i]
N[i]
表示:下边界中第i段的斜率为A[i],投影到x轴的范围是(N[i-1],N[i]]
其中,必有
A[0]>A[1]>A[2]>...
N[0]<N[1]<N[2]<...
并且,N[i]=(C[i+1]-A[i+1]-C[i]+A[i])/(A[i]-A[i+1])
这样,对于落到(N[i-1],N[i]]这个区间内的x,最小值就是A[i]*x*x+B[i]*x+C[i]
\ /
\ /
0 2
\ /
\ /
=======1======
n[0] n[1]
每次查找x点的最值就是找到i,满足N[i-1]<(x+1)^2<=N[i],然后输出
A[i]*x*x+2*A[i]*x+C[i]就好了。
如何维护这两个序列呢?
很显然,用某种数据结构维护以A[i]为关键字的队列,然后套公式更新N[i]
如果发现N[i]>=N[i+1],那么把第i段删除,更新N[i-1]。
也就是:
I a b c:
插入 a c 到 list,设插入他的位置是第i个
计算 N[i],N[i-1]
从i依次往后遍历,如果某个j满足B[j]>=B[j+1],那么删除第j个,计算N[j-1]
从i再依次往前遍历,如果某个j满足B[j]>=B[j+1],那么删除第j个,计算N[j-1]
Q x
查找最小i使(x+1)^2<=N[i]
输出A[i]*x*x+2*A[i]*x+C[i]
关于数据结构的选择,我的思考是:
1.他必须要短,否则调不完
2.他必须要灵活,以便拓展(例如快速求前驱、后继)
3.他一定要省内存,有1000000个查询!
综上,我选择了treap和跳跃表,
但是跳跃表的内存很可能要超,于是选择了treap
与普通treap不同,这个treap顺便维护了每个接点的前驱 后继,也就是treap+双链表
关键代码:
struct node{
int a,c,n,weight;
node *next,*pre;
node *lc,*rc;
node(){next=pre=rc=lc=NULL;}
};
int ran(){
static int a=1325;
a+=(a<<7)+1;
a&=0x7fffffff;
return a;
}
struct Treap{
node *root;
Treap(){root=NULL;}
void link(node *p,node *q){
if (p)p->next=q;
if (q)q->pre=p;
}
node* ins(node *&p,node *q,int a,int c){
node *r,*s;
if (!p){
p=etop++;p->a=a;p->c=c;p->weight=ran();
if (q){
if (q->lc==p){
r=q;
s=r->pre;
link(s,p);link(p,r);
}else if (q->rc==p){
r=q;
s=r->next;
link(r,p);link(p,s);
}
}
return p;
}else{
if (p->a==a){
if (p->c<=c)return p;
else return p->c=c,p;
}else if (a>p->a){
r=ins(p->lc,p,a,c);
if (p->lc->weight>p->weight){
s=p->lc;
p->lc=s->rc;s->rc=p;p=s;
}
}else{
r=ins(p->rc,p,a,c);
if (p->rc->weight>p->weight){
s=p->rc;
p->rc=s->lc;s->lc=p;p=s;
}
}
return r;
}
}
node* ins(int a,int c){
node *r=ins(root,NULL,a,c);
return r;
}
void merge(node *&p,node *a,node *b){
if (!a)p=b;
else if (!b)p=a;
else if (a->weight>b->weight){
p=a;
if (b->a>a->a)merge(a->lc,a->lc,b);
else merge(a->rc,a->rc,b);
}else{
p=b;
if (a->a>b->a)merge(b->lc,b->lc,a);
else merge(b->rc,b->rc,a);
}
}
void del(int a){
node **p=&root,*r,*s;
while ((*p)->a!=a){
if (a>(*p)->a)p=&((*p)->lc);
else p=&((*p)->rc);
}
r=(*p)->pre;s=(*p)->next;
merge(*p,(*p)->lc,(*p)->rc);
link(r,s);
}
node* find2(int y){
node *p=root;
while (1){
if (p->n<y)p=p->rc;
else if (p->pre && p->pre->n>=y)p=p->lc;
else return p;
}
return NULL;
}
}list;
void updata(node *p){if (!p)return;
if (p->next){
p->n=(p->next->c-p->c)/(p->a-p->next->a);
}else p->n=100000001;
}
void Ins(int a,int c){
int x,y;
node *p=list.ins(a,c);
updata(p);updata(p->pre);
if (p->pre && p->pre->n>=p->n){
list.del(p->a);
updata(p->pre);
}else{
while (p->next && p->n>=p->next->n){
list.del(p->next->a);
updata(p);
}
while (p->pre && p->pre->pre && p->pre->pre->n>=p->pre->n){
list.del(p->pre->a);
updata(p->pre);
}
}
}
int Ask(int x){
int y;
if (x<10000)y=x*x;else y=100000000;
node *p=list.find2(y);
return p->a*x*x+p->c;
}
scanf("%s",buf);
if (buf[0]=='I'){
scanf("%d %d %d",&a,&b,&c);
Ins(a,c-a);
}else{
scanf("%d",&x);
x++;
if (x<0)x=-x;
printf("%d\n",Ask(x));
}
Followed by:
Post your reply here: |
All Rights Reserved 2003-2013 Ying Fuchen,Xu Pengcheng,Xie Di
Any problem, Please Contact Administrator