[BZOJ2300][HAOI2011]防线修建(凸包+splay)

211 篇文章 0 订阅
55 篇文章 0 订阅

题目描述

传送门

题解

将删除转化成倒序插入
首先对于初始点建立凸包,每一次插入,在凸包上找到离这个点最近的两个点然后不断向两边删点维护凹凸性
可以用splay维护
这道题还是比较简单的,没有重点并且点的坐标 (0,n) ,所以这只是一个上凸壳,随便维护一下就行了
如果是一个完整的凸包的话,用斜率乱判会更麻烦一些

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 200005

const double eps=1e-9;
int dcmp(double x)
{
    if (x<=eps&&x>=-eps) return 0;
    return (x>0)?1:-1;
}
struct Vector
{
    double x,y;
    int id;
    bool flag;
    Vector(double X=0,double Y=0)
    {
        x=X,y=Y;
    }
    bool operator < (const Vector &a) const
    {
        return x<a.x||(x==a.x&&y<a.y);
    }
};
typedef Vector Point;
Vector operator + (Vector a,Vector b) {return Vector(a.x+b.x,a.y+b.y);}
Vector operator - (Vector a,Vector b) {return Vector(a.x-b.x,a.y-b.y);}
Vector operator * (Vector a,double b) {return Vector(a.x*b,a.y*b);}
Vector operator / (Vector a,double b) {return Vector(a.x/b,a.y/b);}
struct hp{int p;double ans;}ans[N];

double n,x,y,C;
int m,q,cnt,top,sz,root,opt;
Point c[N],p[N],stack[N];
int f[N],ch[N][2],key[N];

double Dot(Vector a,Vector b)
{
    return a.x*b.x+a.y*b.y;
}
double Cross(Vector a,Vector b)
{
    return a.x*b.y-a.y*b.x;
}
double Len(Vector a)
{
    return sqrt(Dot(a,a));
}
void graham()
{
    sort(p+1,p+cnt+1);
    top=0;
    for (int i=1;i<=cnt;++i)
    {
        while (top>1&&dcmp(Cross(stack[top]-stack[top-1],p[i]-stack[top-1]))>=0)
            --top;
        stack[++top]=p[i];
    }
    int k=top;
    for (int i=cnt-1;i>=1;--i)
    {
        while (top>k&&dcmp(Cross(stack[top]-stack[top-1],p[i]-stack[top-1]))>=0)
            --top;
        stack[++top]=p[i];
    }
    if (cnt>1) --top;
}
void clear(int x)
{
    f[x]=ch[x][0]=ch[x][1]=key[x]=0;
}
int build(int l,int r,int fa)
{
    if (l>r) return 0;
    int mid=(l+r)>>1;
    int now=++sz;
    f[now]=fa;key[now]=stack[mid].id;
    int lch=build(l,mid-1,now);
    int rch=build(mid+1,r,now);
    ch[now][0]=lch;ch[now][1]=rch;
    return now;
}
int get(int x)
{
    return ch[f[x]][1]==x;
}
void rotate(int x)
{
    int old=f[x],oldf=f[old],wh=get(x);
    ch[old][wh]=ch[x][wh^1];
    if (ch[old][wh]) f[ch[old][wh]]=old;
    ch[x][wh^1]=old;
    f[old]=x;
    if (oldf) ch[oldf][ch[oldf][1]==old]=x;
    f[x]=oldf;
}
void splay(int x,int tar)
{
    for (int fa;(fa=f[x])!=tar;rotate(x))
        if (f[fa]!=tar)
            rotate((get(x)==get(fa))?fa:x);
    if (!tar) root=x;
}
int find(int id)
{
    int now=root,ans=0;
    while (1)
    {
        if (dcmp(c[key[now]].x-c[id].x)<=0)
        {
            ans=now;
            if (ch[now][1]) now=ch[now][1];
            else break;
        }
        else
        {
            if (ch[now][0]) now=ch[now][0];
            else break;
        }
    }
    splay(ans,0);
    return ans;
}
int pre()
{
    if (!ch[root][0]) return 0;
    int now=ch[root][0];
    while (ch[now][1]) now=ch[now][1];
    return now;
}
int nxt()
{
    if (!ch[root][1]) return 0;
    int now=ch[root][1];
    while (ch[now][0]) now=ch[now][0];
    return now;
}
void del(int x)
{
    splay(x,0);
    if (!ch[root][0]&&!ch[root][1]) {root=0;return;}
    if (!ch[root][0])
    {
        int oldroot=root;
        root=ch[root][1];
        f[root]=0;
        clear(oldroot);
        return;
    }
    if (!ch[root][1])
    {
        int oldroot=root;
        root=ch[root][0];
        f[root]=0;
        clear(oldroot);
        return;
    }
    int oldroot=root;
    int leftbig=pre();splay(leftbig,0);
    ch[root][1]=ch[oldroot][1];
    f[ch[root][1]]=root;
    clear(oldroot);
    return;
}

int main()
{
    scanf("%lf%lf%lf",&n,&x,&y);
    scanf("%d",&m);
    for (int i=1;i<=m;++i) scanf("%lf%lf",&c[i].x,&c[i].y),c[i].id=i;
    c[++m].x=0,c[m].y=0,c[m].id=m;c[++m].x=n,c[m].y=0,c[m].id=m;c[++m].x=x,c[m].y=y,c[m].id=m;
    scanf("%d",&q);
    for (int i=1;i<=q;++i)
    {
        scanf("%d",&opt);
        if (opt==1)
        {
            scanf("%d",&ans[i].p);
            c[ans[i].p].flag=true;
        }
    }
    for (int i=1;i<=m;++i)
        if (!c[i].flag) p[++cnt]=c[i];
    graham();
    for (int i=2;i<=top;++i) C+=Len(stack[i]-stack[i-1]);
    root=build(1,top,0);
    for (int i=q;i>=1;--i)
    {
        if (ans[i].p)
        {
            Point P=c[ans[i].p];
            int aa,bb,cc,dd;

            aa=find(P.id);
            cc=pre();
            bb=nxt();
            splay(bb,0);
            dd=nxt();

            if (aa&&bb&&dcmp(Cross(P-c[key[aa]],c[key[bb]]-P))>=0) continue;
            else
            {
                C-=Len(c[key[aa]]-c[key[bb]]);
                C+=Len(P-c[key[aa]]);
                C+=Len(P-c[key[bb]]);
            }
            while (aa&&cc&&dcmp(Cross(c[key[aa]]-c[key[cc]],P-c[key[aa]]))>=0)
            {
                C-=Len(P-c[key[aa]]);
                C-=Len(c[key[aa]]-c[key[cc]]);
                C+=Len(P-c[key[cc]]);
                del(aa);
                aa=cc;splay(aa,0);
                cc=pre();
            }
            while (bb&&dd&&dcmp(Cross(c[key[bb]]-P,c[key[dd]]-c[key[bb]]))>=0)
            {
                C-=Len(P-c[key[bb]]);
                C-=Len(c[key[bb]]-c[key[dd]]);
                C+=Len(P-c[key[dd]]);
                del(bb);
                bb=dd;splay(bb,0);
                dd=nxt();
            }
            splay(aa,0);
            splay(bb,aa);
            ch[ch[root][1]][0]=++sz;
            f[sz]=ch[root][1];
            key[sz]=ans[i].p;
        }
        else ans[i].ans=C;
    }
    for (int i=1;i<=q;++i)
        if (!ans[i].p) printf("%.2lf\n",ans[i].ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值