bzoj 2300 [HAOI2011]防线修建 splay维护凸包

本文介绍使用Splay树进行凸包维护的方法,通过将删除操作转换为插入操作来实现高效的凸包更新。该方法适用于解决包含动态增删点操作的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

正常的splay维护凸包不支持删点,把操作倒过来变成加点。
HAOI真心良心呀,边界都给好了。。。

#include <bits/stdc++.h>
using namespace std;
#define N 110000
#define M 210000
#define which(x) (ch[fa[x]][1]==x)
int d,n,Q,root;
int X[N],Y[N],vis[N],tp[M],v[M];
int pre[N],nex[N],fa[N],ch[N][2];
double ans,fin[M];
void rotate(int x)
{
    int y=fa[x],k=which(x);
    ch[y][k]=ch[x][k^1];
    ch[x][k^1]=y;
    ch[fa[y]][which(y)]=x;

    fa[x]=fa[y];fa[y]=x;
    fa[ch[y][k]]=y;
}
void splay(int x,int tar)
{
    while(fa[x]!=tar)
    {
        int y=fa[x];
        if(fa[y]==tar)rotate(x);
        else
        {
            if(which(x)^which(y))rotate(x);
            else rotate(y);
            rotate(x);
        }
    }
    if(!tar)root=x;
}
void f_pre(int x,int v,int &ret)
{
    if(!x)return;
    if(X[x]<=v)
    {
        ret=X[x]>X[ret] ? x:ret;
        f_pre(ch[x][1],v,ret);
    }
    else f_pre(ch[x][0],v,ret);
}
void f_nex(int x,int v,int &ret)
{
    if(!x)return;
    if(X[x]>v)
    {
        ret=X[x]<X[ret] ? x:ret;
        f_nex(ch[x][0],v,ret);
    }
    else f_nex(ch[x][1],v,ret);
}
double dis(int x,int y)
{return sqrt((X[x]-X[y])*(X[x]-X[y])+(Y[x]-Y[y])*(Y[x]-Y[y]));}
void del(int x)
{
    splay(pre[x],0);
    splay(nex[x],root);
    ch[nex[x]][0]=0;
    nex[pre[x]]=nex[x];pre[nex[x]]=pre[x];
}
void ins(int x)
{
    int l=1,r=2;
    f_pre(root,X[x],l);f_nex(root,X[x],r);
    if((Y[x]-Y[l])*(X[r]-X[l])<(Y[r]-Y[l])*(X[x]-X[l]))return;
    ans-=dis(l,r);
    while(l!=1&&(Y[l]-Y[pre[l]])*(X[x]-X[l])<(Y[x]-Y[l])*(X[l]-X[pre[l]]))
        del(l),ans-=dis(l,pre[l]),l=pre[l];
    while(r!=2&&(Y[r]-Y[x])*(X[nex[r]]-X[r])<(Y[nex[r]]-Y[r])*(X[r]-X[x]))
        del(r),ans-=dis(r,nex[r]),r=nex[r];
    nex[l]=x;pre[x]=l;
    pre[r]=x;nex[x]=r;
    ans+=dis(l,x);ans+=dis(r,x);
    splay(pre[x],0);
    splay(nex[x],root);
    ch[nex[x]][0]=x;fa[x]=nex[x];
    splay(x,0);
}
int main()
{
    scanf("%d%d%d",&d,&X[3],&Y[3]);
    scanf("%d",&n);X[2]=d;
    for(int i=1;i<=n;i++)scanf("%d%d",&X[i+3],&Y[i+3]);
    scanf("%d",&Q);
    for(int i=1;i<=Q;i++)
    {
        scanf("%d",&tp[i]);
        if(tp[i]==1)scanf("%d",&v[i]),vis[v[i]]=1;
    }
    pre[3]=1;nex[3]=2;root=3;
    ch[3][0]=1;ch[3][1]=2;fa[1]=fa[2]=3;
    ans=dis(3,1)+dis(3,2);
    for(int i=1;i<=n;i++)
        if(!vis[i])ins(i+3);
    for(int i=Q;i>=1;i--)   
    {
        if(tp[i]==1)ins(v[i]+3);
        else fin[i]=ans;
    }
    for(int i=1;i<=Q;i++)
        if(tp[i]==2)printf("%.2lf\n",fin[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值