Typesetting math: 100%

[bzoj2300] [HAOI2011]防线修建

0|1Description


近来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)和首都这三个城市是一定需要保护的。

img

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

img

0|1Input


第一行,三个整数n,x,y分别表示河边城市和首都是(0,0),(n,0),(x,y)。

第二行,一个整数m。

接下来m行,每行两个整数a,b表示A国的一个非首都非河边城市的坐标为(a,b)。

再接下来一个整数q,表示修改和询问总数。

接下来q行每行要么形如1 i,要么形如2,分别表示撤销第i个城市的保护和询问。

0|1Output


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

0|1Sample Input


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

0|1Sample Output


6.47 5.84 4.47

0|1Solution


考虑把操作顺序改一下,我们反着做,就变成了维护凸包,支持加点。

由于是个上凸壳,可以直接用一个set维护凸包上的点的x坐标。

然后每次加点lower_bound找到x最近的一条边,然后判一下在不在凸包内。

若不在凸包内,就往两边暴力判满不满足上凸壳性质,若不满足,就删点。

每个点只会被至多加一次,删一次,时间复杂度O(nlogn)

#include<bits/stdc++.h> using namespace std; void read(int &x) { x=0;int f=1;char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f; } void print(int x) { if(x<0) putchar('-'),x=-x; if(!x) return ;print(x/10),putchar(x%10+48); } void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');} const int maxn = 2e6+10; struct point { int x,y; int operator ^ (const point &rhs) const {return x*rhs.y-rhs.x*y;} int operator < (const point &rhs) const {return x<rhs.x||(x==rhs.x&&y<rhs.y);} point operator - (const point &rhs) const {return (point){x-rhs.x,y-rhs.y};} }a[maxn],sta[maxn],r[maxn]; int cnt,op[maxn],w[maxn],vis[maxn],top,m; double ans; set<point > s; #define sqr(x) ((x)*(x)) double dis(point x,point y) {return sqrt(sqr(x.x-y.x)+sqr(y.y-x.y));} void make() { int num=0; for(int i=1;i<=cnt;i++) if(!vis[i]) r[++num]=a[i]; sort(r+1,r+num+1);sta[++top]=r[1],sta[++top]=r[2]; for(int i=3;i<=num;i++) { while(top>1&&((r[i]-sta[top-1])^(sta[top]-sta[top-1]))<=0) top--; sta[++top]=r[i]; } s.insert(sta[1]); for(int i=2;i<=top;i++) s.insert(sta[i]),ans+=dis(sta[i],sta[i-1]); } double res[maxn+8]; int Top; void solve() { for(int i=m;i;i--) { if(op[i]==2) res[++Top]=ans; else { point x=a[w[i]+3]; set <point > :: iterator it,it2,pre,nxt; it=s.lower_bound(x);it--; it2=it;it2++; if(((x-(*it))^((*it2)-(*it)))>=0) continue; ans-=dis(*it,*it2); while(it!=s.begin()) { pre=it;pre--; if(((x-(*it))^((*it)-(*pre)))>0) break; ans-=dis(*it,*pre); it--;pre++;s.erase(pre); } while(it2!=s.end()) { nxt=it2;nxt++; if((((*it2)-x)^((*nxt)-(*it2)))<0) break; ans-=dis(*it2,*nxt); nxt--;it2++;s.erase(nxt); } it=s.lower_bound(x); ans+=dis(x,*it); it--,ans+=dis(x,*it); s.insert(x); } } for(int i=Top;i;i--) printf("%.2lf\n",res[i]); } int main() { int n,x,y;read(n),read(x),read(y); a[++cnt]=(point){0,0}; a[++cnt]=(point){n,0}; a[++cnt]=(point){x,y}; read(n); for(int i=1;i<=n;i++) read(x),read(y),a[++cnt]=(point){x,y}; read(m); for(int i=1;i<=m;i++) { read(op[i]);if(op[i]==1) read(w[i]),vis[w[i]+3]=1; } make();solve(); return 0; }

__EOF__

作  者Hyscere
出  处https://www.cnblogs.com/hbyer/p/10324119.html
关于博主:编程路上的小学生,热爱技术,喜欢专研。评论和私信会在第一时间回复。或者直接私信我。
版权声明:署名 - 非商业性使用 - 禁止演绎,协议普通文本 | 协议法律文本
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!

posted @   Hyscere  阅读(237)  评论(0编辑  收藏  举报
编辑推荐:
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
阅读排行:
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!
点击右上角即可分享
微信分享提示