题目描述

近来A国和B国的矛盾激化,为了预防不测,A国准备修建一条长长的防线,当然修建防线的话,肯定要把需要保护的城市修在防线内部了。可是A国上层现在还犹豫不决,到底该把哪些城市作为保护对象呢?又由于A国的经费有限,所以希望你能帮忙完成如下的一个任务:
1.给出你所有的A国城市坐标
2.A国上层经过讨论,考虑到经济问题,决定取消对i城市的保护,也就是说i城市不需要在防线内了
3.A国上层询问对于剩下要保护的城市,修建防线的总经费最少是多少
你需要对每次询问作出回答。注意单位1长度的防线花费为1。
A国的地形是这样的,形如下图,x轴是一条河流,相当于一条天然防线,不需要你再修建
A国总是有两个城市在河边,一个点是(0,0),一个点是(n,0),其余所有点的横坐标均大于0小于n,纵坐标均大于0。A国有一个不在(0,0)和(n,0)的首都。(0,0),(n,0)和首都这三个城市是一定需要保护的。

上图中,A,B,C,D,E点为A国城市,且目前都要保护,那么修建的防线就会是A-B-C-D,花费也就是线段AB的长度+线段BC的长度+线段CD的长度,如果,这个时候撤销B点的保护,那么防线变成下图

输入

第一行,三个整数n,x,y分别表示河边城市和首都是(0,0),(n,0),(x,y)。
第二行,一个整数m。
接下来m行,每行两个整数a,b表示A国的一个非首都非河边城市的坐标为(a,b)。
再接下来一个整数q,表示修改和询问总数。
接下来q行每行要么形如1 i,要么形如2,分别表示撤销第i个城市的保护和询问。

输出

对于每个询问输出1行,一个实数v,表示修建防线的花费,保留两位小数

样例输入

4 2 1
2
1 2
3 2
5
2
1 1
2
1 2
2

样例输出

6.47
5.84
4.47

提示

m<=100000,q<=200000,n>1
所有点的坐标范围均在10000以内, 数据保证没有重点
 
题目要求动态维护一个凸包,但观察到修改操作只有删点。
如果每次删除一个点的话,可能会有许多点重新变成凸包上的点,无法保证时间复杂度。
所以我们将询问离线,然后倒着做,将删点变成加点。
先对刚开始就有的所有点(即没被删除的所有点)维护一个凸包,然后对凸包上的点以横坐标为权值建立平衡树。
每次插入一个点时,在平衡树上二分找到该插入的位置,并判断这个点是否在凸包上方(如果在下方,那么这个点不会影响凸包)。
然后分别向左/右枚举凸包上的点,像刚开始求凸包时一样用叉积判断原凸包上的点是否在新插入点与之前点的下方,然后将这些点从凸包上删除即可。
因为每个点最多会被删除一次,所以时间复杂度为$O(mlog_{m}+q)$。

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
struct lty
{
int x,y;
lty(int X=0,int Y=0){x=X,y=Y;}
bool operator <(const lty &a)const{return x==a.x?y<a.y:x<a.x;}
lty operator -(const lty &a)const{return lty(x-a.x,y-a.y);}
}p[100010],s[100010],st[100010],tr[100010];
int cnt;
int top;
int tot;
int x,y;
int root;
int n,m,Q;
int ls[100010];
int rs[100010];
int r[100010];
int size[100010];
double ans;
int a,b,c,d;
int vis[100010];
struct yzl
{
int opt,id;
double res;
}q[200010];
ll cross(lty a,lty b)
{
return 1ll*a.x*b.y-1ll*a.y*b.x;
}
int newnode(int x,int y)
{
int rt=++cnt;
tr[rt].x=x;
tr[rt].y=y;
r[rt]=rand();
size[rt]=1;
return rt;
}
void pushup(int rt)
{
size[rt]=size[ls[rt]]+size[rs[rt]]+1;
}
double dis(lty a,lty b)
{
lty c=a-b;
return sqrt(1ll*c.x*c.x+1ll*c.y*c.y);
}
int merge(int x,int y)
{
if(!x||!y)
{
return x+y;
}
if(r[x]<r[y])
{
rs[x]=merge(rs[x],y);
pushup(x);
return x;
}
else
{
ls[y]=merge(x,ls[y]);
pushup(y);
return y;
}
}
void split_value(int rt,int &x,int &y,int k,int z)
{
if(!rt)
{
x=y=0;
return ;
}
if(tr[rt].x>k||(tr[rt].x==k&&tr[rt].y>z))
{
y=rt;
split_value(ls[rt],x,ls[y],k,z);
}
else
{
x=rt;
split_value(rs[rt],rs[x],y,k,z);
}
pushup(rt);
}
void split_left(int rt,int &x,int &y,int k)
{
if(!rt)
{
x=y=0;
return ;
}
if(size[ls[rt]]>=k)
{
y=rt;
split_left(ls[rt],x,ls[y],k);
}
else
{
x=rt;
split_left(rs[rt],rs[x],y,k-size[ls[rt]]-1);
}
pushup(rt);
}
void split_right(int rt,int &x,int &y,int k)
{
if(!rt)
{
x=y=0;
return ;
}
if(size[rs[rt]]>=k)
{
x=rt;
split_right(rs[rt],rs[x],y,k);
}
else
{
y=rt;
split_right(ls[rt],x,ls[y],k-size[rs[rt]]-1);
}
pushup(rt);
}
int build(int l,int r)
{
if(l==r)
{
return newnode(st[l].x,st[l].y);
}
int mid=(l+r)>>1;
return merge(build(l,mid),build(mid+1,r));
}
int main()
{
srand(12378);
scanf("%d%d%d",&n,&x,&y);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&p[i].x,&p[i].y);
}
p[m+1].x=x,p[m+1].y=y;
p[m+2].x=0,p[m+2].y=0;
p[m+3].x=n,p[m+3].y=0;
m+=3;
scanf("%d",&Q);
for(int i=1;i<=Q;i++)
{
scanf("%d",&q[i].opt);
if(q[i].opt==1)
{
scanf("%d",&q[i].id);
vis[q[i].id]=1;
}
}
for(int i=1;i<=m;i++)
{
if(!vis[i])
{
s[++tot]=p[i];
}
}
sort(s+1,s+1+tot);
for(int i=1;i<=tot;i++)
{
while(top>1&&cross(st[top-1]-st[top],s[i]-st[top])<=0)
{
top--;
}
st[++top]=s[i];
}
root=build(1,top);
for(int i=2;i<=top;i++)
{
ans+=dis(st[i-1],st[i]);
}
for(int i=Q;i>=1;i--)
{
if(q[i].opt==2)
{
q[i].res=ans;
}
else
{
int now=q[i].id;
split_value(root,a,d,p[now].x,p[now].y);
split_right(a,a,b,1);
split_left(d,c,d,1);
if(cross(tr[b]-p[now],tr[c]-p[now])<=0)
{
a=merge(a,b);
d=merge(c,d);
root=merge(a,d);
continue;
}
ans-=dis(tr[b],tr[c]);
a=merge(a,b);
d=merge(c,d);
split_right(a,a,c,1);
split_right(a,a,b,1);
while(b&&c&&cross(tr[b]-tr[c],p[now]-tr[c])<=0)
{
ans-=dis(tr[b],tr[c]);
c=b;
split_right(a,a,b,1);
}
ans+=dis(p[now],tr[c]);
a=merge(a,b);
a=merge(a,c);
split_left(d,b,d,1);
split_left(d,c,d,1);
while(b&&c&&cross(p[now]-tr[b],tr[c]-tr[b])<=0)
{
ans-=dis(tr[b],tr[c]);
b=c;
split_left(d,c,d,1);
}
ans+=dis(p[now],tr[b]);
d=merge(c,d);
d=merge(b,d);
root=merge(merge(a,newnode(p[now].x,p[now].y)),d);
}
}
for(int i=1;i<=Q;i++)
{
if(q[i].opt==2)
{
printf("%.2f\n",q[i].res);
}
}
}

BZOJ2300[HAOI2011]防线修建——非旋转treap+凸包(平衡树动态维护凸包)的更多相关文章

  1. 2019.02.21 bzoj2300: [HAOI2011]防线修建(set+凸包)

    传送门 题意:动态维护凸包周长. 思路: 见这篇求面积的吧反正都是一个套路. 代码: #include<bits/stdc++.h> #define int long long #defi ...

  2. bzoj千题计划236:bzoj2300: [HAOI2011]防线修建

    http://www.lydsy.com/JudgeOnline/problem.php?id=2300 维护动态凸包,人懒用的set 用叉积判断,不要用斜率 #include<set> ...

  3. BZOJ2300: [HAOI2011]防线修建

    题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2300 (我只是在发以前写过的题.. 因为题目没说强制在线,所以离线乱搞就可以了.先把点删掉然后 ...

  4. 非旋转Treap——普通平衡树

    扔板跑…… #include<bits/stdc++.h> #define N 100010 #define mp make_pair using namespace std; typed ...

  5. 【BZOJ2300】[HAOI2011]防线修建 set维护凸包

    [BZOJ2300][HAOI2011]防线修建 Description 近来A国和B国的矛盾激化,为了预防不测,A国准备修建一条长长的防线,当然修建防线的话,肯定要把需要保护的城市修在防线内部了.可 ...

  6. 【BZOJ 2300】 2300: [HAOI2011]防线修建 (动态凸包+set)

    2300: [HAOI2011]防线修建 Description 近来A国和B国的矛盾激化,为了预防不测,A国准备修建一条长长的防线,当然修建防线的话,肯定要把需要保护的城市修在防线内部了.可是A国上 ...

  7. BZOJ 2300: [HAOI2011]防线修建( 动态凸包 )

    离线然后倒着做就变成了支持加点的动态凸包...用平衡树维护上凸壳...时间复杂度O(NlogN) --------------------------------------------------- ...

  8. 【题解】P2521 [HAOI2011]防线修建(动态凸包)

    [题解]P2521 [HAOI2011]防线修建(动态凸包) 凸包是易插入不好删除的东西,按照剧情所以我们时光倒流 然后问题就是维护凸包的周长,支持加入 本来很简单,但是计算几何就是一些小地方经验不足 ...

  9. 「BZOJ2300」[HAOI2011] 防线修建

    传送门 操作离线之后倒着做,只有加点操作. 用set动态维护凸包即可. //Achen #include<algorithm> #include<iostream> #incl ...

随机推荐

  1. mysql_8.0.12环境配置

    1. 官网下载mysql_8.0.12免安装包,解压到你存放的地方: 2. 配置环境变量(把bin的文件夹弄进系统path里面): 3. 在解压的根路径中,查看是否含有my.ini文件,没有就新建一个 ...

  2. SLAM+语音机器人DIY系列:(二)ROS入门——5.编写简单的消息发布器和订阅器

    摘要 ROS机器人操作系统在机器人应用领域很流行,依托代码开源和模块间协作等特性,给机器人开发者带来了很大的方便.我们的机器人“miiboo”中的大部分程序也采用ROS进行开发,所以本文就重点对ROS ...

  3. 使用 FFT 分析周期性数据

    可以使用傅里叶变换来分析数据中的变化,例如一个时间段内的自然事件. 天文学家使用苏黎世太阳黑子相对数将几乎 300 年的太阳黑子的数量和大小制成表格.对大约 1700 至 2000 年间的苏黎世数绘图 ...

  4. [C#] C# 与 Nessus 交互,动态构建扫描任务计划

    C# 与 Nessus 交互,动态构建扫描任务计划 目录 什么是 Nessus? 创建会话类 NessusSession 登录测试 创建操作类 NessusManager 操作测试 什么是 Nessu ...

  5. phpcms V9 二次开发------(获取点击数详解)

    关于phpcms V9的点击数的使用应该有不少数是直接调用网上搜索到的代码,但是对于一些想要深入研究开发的人来说,看到网上的代码后更是不解,本人这几天看了看,了解了一些东西,在这里写出来分享一下,首先 ...

  6. React 16.x 新特性思维导图

    React 16版本相对于以前的版本做了很大的改动,下面是我整理的React 16.x 新特性的思维导图文件,欢迎围观和指导:

  7. 各种raid对比

    级别 最少单元 特征 冗余 性能 空间利用率 综合评价 RAID0 1 分片分散存入 否 读写2倍 100% 分散存储,任何一块坏掉数据则不完整 RAID1 2 相同数据存入2个磁盘 是 写不变,读快 ...

  8. iOS NFC

    #import <CoreNFC/CoreNFC.h> @interface ViewController ()<NFCNDEFReaderSessionDelegate> @ ...

  9. 数据库MySQL和Redis实践

    1.关于数据库设计的那些事 2.MySQL 3.Redis

  10. codeforces div2 220 解题

    这套题我只写了a, b, c..  对不起,是我太菜了. A:思路:就是直接简化为一个矩阵按照特定的步骤从一个顶角走到与之对应的对角线上的顶角.如图所示. 解释一下特定的步骤,就像马走日,象走田一样. ...