[HAOI2011]防线修建
太懒了,懒得描述题面了。。。
凸包删点谁受得了,凸包就没法维护了,所以我们倒着看,把删点改为加点,每次加一个可能导致一些点从凸包上消失,但每个点消失一次就回不来了,所以每个点访问一次,可以保证复杂度。
还好这题是个壳不是个包,否则比较毒瘤了。
每次加入一个点,首先需要考虑它是否在凸包中,如果在就 \(return\) ,如果不考虑这个的话,可能这个点左边和右边确实维护成了凸壳,但是自己这个位置是凹的。然后向左看,将叉积小于 \(0\) 的中间点删除,向右看,将叉积大于 \(0\) 的中间点删除。
用一颗平衡树维护凸壳。由于博主太菜,\(set\) 用的不熟,怕那些奇奇怪怪的边界问题,所以手写了个 \(Splay\),更灵活但是代码稍长点。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#define QWQ cout<<"QwQ"<<endl;
#define ll long long
#include <vector>
#include <queue>
#include <stack>
#include <map>
#define ls son[x][0]
#define rs son[x][1]
using namespace std;
const int N=401010;
const int qwq=303030;
const int inf=0x3f3f3f3f;
int R;
int n,m;
struct E{
double x,y;
E(double xx=0,double yy=0) { x=xx; y=yy; }
}val[N],a[N];
int root = 1,tot;
int son[N][2],fa[N];
int cz[N],cx[N],vis[N];
double ANS,ans[N];
inline int read() {
int lyy = 0, zbk = 1; char z7z = getchar();
while(z7z<'0' || z7z>'9') { if(z7z=='-') zbk = -1; z7z = getchar(); }
while(z7z>='0'&&z7z<='9') { lyy = lyy * 10 + z7z - '0'; z7z = getchar(); }
return lyy * zbk;
}
inline double cha(E aa,E bb,E cc) { E A = E(bb.x-aa.x,bb.y-aa.y), B = E(cc.x-bb.x,cc.y-bb.y); return A.x*B.y - A.y*B.x; }
inline double ju(int aa,int bb) { E A = val[aa], B = val[bb]; return sqrt( (A.x-B.x)*(A.x-B.x) + (A.y-B.y)*(A.y-B.y) ); }
inline bool touhou(int x) { return son[fa[x]][1]==x; }
inline void rotate(int x) {
int y = fa[x], z = fa[y], k = touhou(x), w = son[x][k^1];
son[z][touhou(y)] = x; son[x][k^1] = y; son[y][k] = w;
fa[x] = z; fa[y] = x; fa[w] = y;
}
inline void Splay(int x,int goal) {
while(fa[x] != goal) {
int y = fa[x], z = fa[y];
if(z!=goal) {
if(touhou(x)==touhou(y)) rotate(y);
else rotate(x);
}
rotate(x);
}
if(!goal) root = x;
}
inline int qian(int x) { Splay(x,0); if(!ls) return -1; x = ls; while(rs) x = rs; return x; }
inline int hou(int x) { Splay(x,0); if(!rs) return -1; x = rs; while(ls) x = ls; return x; }
inline void del(int x) { int y = qian(x), z = hou(x); Splay(y,0); Splay(z,y); son[z][0] = fa[x] = 0; }
void insert(E u) {
int v = root, f;
while(v) f = v, v = son[v][ val[v].x<u.x ];
v = ++tot; val[v] = u;
fa[v] = f; son[f][ val[f].x<u.x ] = v;
Splay(v,0);
int lv = qian(v), rv = hou(v);
if(cha(val[lv],val[v],val[rv])>=0) { del(v); return ; }
ANS -= ju(lv,rv); ANS += ju(v,lv); ANS += ju(v,rv);
while(2333) {
int aa = qian(v), bb = qian(aa);
if(bb==-1) break;
if(cha(val[bb],val[aa],val[v])>=0) {
ANS -= ju(aa,bb) + ju(aa,v); del(aa); ANS += ju(bb,v);
}
else break;
}
while(2333) {
int aa = hou(v), bb = hou(aa);
if(bb==-1) break;
if(cha(val[v],val[aa],val[bb])>=0) {
ANS -= ju(aa,bb) + ju(aa,v); del(aa); ANS += ju(bb,v);
}
else break;
}
}
int main() {
int x,y;
R = read(); x = read(); y = read();
root = 1; fa[2] = fa[3] = 1;
son[1][0] = 2; son[1][1] = 3; tot = 3;
val[2] = E(0,0); val[3] = E(R,0); val[1] = E(x,y);
ANS += ju(1,2); ANS += ju(1,3);
n = read();
for(int i=1;i<=n;i++) a[i].x = read(), a[i].y = read();
m = read();
for(int i=1;i<=m;i++) {
cz[i] = read();
if(cz[i]==1) {
cx[i] = read();
vis[ cx[i] ] = 1;
}
}
for(int i=1;i<=n;i++) if(!vis[i]) insert(a[i]);
for(int i=m;i>=1;i--) {
if(cz[i]==2) ans[i] = ANS;
else insert(a[ cx[i] ]);
}
for(int i=1;i<=m;i++) if(cz[i]==2) printf("%.2lf\n",ans[i]);
return 0;
}
感觉考场上手敲平衡树很不吃香,所以看了别的大佬的题解重写了个 \(set\) 版的:
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#define QWQ cout<<"QwQ"<<endl;
#define ll long long
#include <vector>
#include <queue>
#include <stack>
#include <set>
using namespace std;
const int N=401010;
const int qwq=303030;
const int inf=0x3f3f3f3f;
int n,m;
int cz[N],cx[N],vis[N];
double ans[N],ANS;
struct E{
int x,y;
E(int xx=0,int yy=0) { x=xx; y=yy; }
} a[N];
inline E operator - (E aa,E bb) { return E(aa.x-bb.x,aa.y-bb.y); }
inline int operator * (E aa,E bb) { return aa.x*bb.y-aa.y*bb.x; }
inline bool operator < (E aa,E bb) { return (aa.x==bb.x)?(aa.y<bb.y):(aa.x<bb.x); }
set <E> S;
inline int read() {
int lyy = 0, zbk = 1; char z7z = getchar();
while(z7z<'0' || z7z>'9') { if(z7z=='-') zbk = -1; z7z = getchar(); }
while(z7z>='0'&&z7z<='9') { lyy = lyy * 10 + z7z - '0'; z7z = getchar(); }
return lyy * zbk;
}
inline double ju(E aa) { return sqrt(aa.x*aa.x+aa.y*aa.y); }
inline void Push(E x) {
set<E>::iterator r = S.lower_bound(x), l = r, mid;
--l;
if( (*l - x) * (*r - x) < 0) return;
ANS -= ju(*r - *l);
while(2333) {
mid = r; ++r;
if(r==S.end() || (*mid - x) * (*r - x) <= 0) break;
ANS -= ju(*r - *mid); S.erase(mid);
}
while(2333) {
mid = l; --l;
if(mid==S.begin() || (*mid - *l) * (x - *l) <= 0) break;
ANS -= ju(*mid - *l); S.erase(mid);
}
S.insert(x);
l = r = S.find(x);
--l; ++r;
ANS += ju(x - *l) + ju(x - *r);
}
int main() {
int R,X,Y;
R = read(); X = read(); Y = read();
S.insert(E(0,0));
S.insert(E(R,0));
S.insert(E(X,Y));
ANS += ju( E(X,Y) ) + ju( E(X-R,Y) );
n = read();
for(int i=1;i<=n;i++) a[i].x = read(), a[i].y = read();
m = read();
for(int i=1;i<=m;i++) {
cz[i] = read(); if(cz[i]==2) continue;
cx[i] = read(); vis[cx[i]] = 1;
}
for(int i=1;i<=n;i++) if(!vis[i]) Push(a[i]);
for(int i=m;i>=1;i--) {
if(cz[i]==2) ans[i] = ANS;
else Push(a[cx[i]]);
}
for(int i=1;i<=m;i++) if(cz[i]==2) printf("%.2lf\n",ans[i]);
return 0;
}