BZOJ 2300

题目传送门

凸包 动态删点并询问当前凸包周长

这道题在线应该也可以,但离线一下就比较好想了,动态删点变成了动态加点

先离线并记录哪些城市始终需要保护,然后把初始的三个点加进去,再把始终要保护的城市加进去并维护

然后倒着来依次加点(别人都用Treap Set神马的,尼玛真短…只会SBT…),每次加点的步骤如下:

       1.找到在凸包中的前驱节点(左边的点)l与后继节点(右边的点)r,用叉积判断如果在直线lr内侧就说明在凸包内侧然后跳出,否则说明lr这条边一定要删然后删掉并继续

       2.找到l的前驱ll,如果当前要插入的点在lll连线的外侧说明lll的线段将被覆盖(也就是说l将被覆盖),删掉l这个点,更新答案,从ll继续往下找,重复以上步骤直到找到最左端结点或发现不能覆盖节点l,然后将新连的边加入答案

       3.右端的操作类似

       4.将当前点加入凸包

如果每次修改都重新计算一遍凸包周长肯定慢死,所以开一个全局变量记录当前凸包周长修改时改这个就好了,反正一步步修改每一步只改一条边

求前驱和后继一定注意要找与当前点不同的,要不然不存在直线的

叉积真是个好东西…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
program bzoj_2300;
type point=record x,y:longint; end;
var p:array[1..100000]of point;
q:array[1..200000]of longint;
ans:array[1..200000]of extended;
curans:extended;
n,m,query,i,k,tot,root:longint;
cap:point;
s:array[0..100000]of record lc,rc,size,x,y:longint; end;
flag:array[1..100000]of boolean;

function sml(x1,y1,x2,y2:longint):boolean;
begin
if (x1<>x2) then exit(x1<x2);
exit(y1<y2);
end;

function dis(x1,y1,x2,y2:longint):extended;
begin
exit(sqrt(sqr(x2-x1)+sqr(y2-y1)));
end;

function cross(x1,y1,x2,y2:longint):longint;
begin
exit(x1*y2-x2*y1);
end;

procedure l_rotate(var t:longint);
var k:longint;
begin
k:=s[t].rc;
s[t].rc:=s[k].lc;
s[k].lc:=t;
s[k].size:=s[t].size;
s[t].size:=s[s[t].lc].size+s[s[t].rc].size+1;
t:=k;
end;

procedure r_rotate(var t:longint);
var k:longint;
begin
k:=s[t].lc;
s[t].lc:=s[k].rc;
s[k].rc:=t;
s[k].size:=s[t].size;
s[t].size:=s[s[t].lc].size+s[s[t].rc].size+1;
t:=k;
end;

procedure maintain(var t:longint;flag:boolean);
begin
if not flag then
if s[s[s[t].lc].lc].size>s[s[t].rc].size then r_rotate(t) else
if s[s[s[t].lc].rc].size>s[s[t].rc].size then
begin
l_rotate(s[t].lc);
r_rotate(t);
end else exit;
if flag then
if s[s[s[t].rc].rc].size>s[s[t].lc].size then l_rotate(t) else
if s[s[s[t].rc].lc].size>s[s[t].lc].size then
begin
r_rotate(s[t].rc);
l_rotate(t);
end else exit;
maintain(s[t].lc,false);
maintain(s[t].rc,true);
maintain(t,false);
maintain(t,true);
end;

procedure insert(var t:longint;x,y:longint);
begin
if t=0 then
begin
inc(tot);
t:=tot;
s[t].x:=x;
s[t].y:=y;
s[t].size:=1;
exit;
end;
inc(s[t].size);
if sml(x,y,s[t].x,s[t].y) then
begin
insert(s[t].lc,x,y);
maintain(t,false);
end else
begin
insert(s[t].rc,x,y);
maintain(t,true);
end;
end;

function pred(t,x,y:longint):point;
begin
pred.x:=0;
pred.y:=0;
while t<>0 do
if sml(x,y,s[t].x,s[t].y)or((s[t].x=x)and(s[t].y=y)) then t:=s[t].lc else
begin
pred.x:=s[t].x;
pred.y:=s[t].y;
t:=s[t].rc;
end;
end;

function succ(t,x,y:longint):point;
begin
succ.x:=n;
succ.y:=0;
while t<>0 do
if sml(s[t].x,s[t].y,x,y)or((s[t].x=x)and(s[t].y=y)) then t:=s[t].rc else
begin
succ.x:=s[t].x;
succ.y:=s[t].y;
t:=s[t].lc;
end;
end;

function delete(var t:longint;x,y:longint):longint;
var d:longint;
begin
dec(s[t].size);
if ((s[t].x=x)and(s[t].y=y))or(sml(x,y,s[t].x,s[t].y)and(s[t].lc=0))or(sml(s[t].x,s[t].y,x,y)and(s[t].rc=0)) then
begin
delete:=t;
if (s[t].lc=0)or(s[t].rc=0) then t:=s[t].lc+s[t].rc else
begin
d:=delete(s[t].lc,x,y+1);
s[t].x:=s[d].x;
s[t].y:=s[d].y;
end;
end else
if sml(x,y,s[t].x,s[t].y) then exit(delete(s[t].lc,x,y)) else exit(delete(s[t].rc,x,y));
end;

procedure addpoint(x,y:longint);
var l,r,ll,rr:point;
begin
l:=pred(root,x,y);
r:=succ(root,x,y);
if cross(x-l.x,y-l.y,r.x-l.x,r.y-l.y)>=0 then exit;
curans:=curans-dis(l.x,l.y,r.x,r.y);
while (l.x<>0)and(l.y<>0) do
begin
ll:=pred(root,l.x,l.y);
if cross(x-l.x,y-l.y,ll.x-l.x,ll.y-l.y)>=0 then
begin
curans:=curans-dis(l.x,l.y,ll.x,ll.y);
delete(root,l.x,l.y);
l:=ll;
end else break;
end;
curans:=curans+dis(l.x,l.y,x,y);
while (r.x<n)and(r.y<>0) do
begin
rr:=succ(root,r.x,r.y);
if cross(x-r.x,y-r.y,rr.x-r.x,rr.y-r.y)<=0 then
begin
curans:=curans-dis(r.x,r.y,rr.x,rr.y);
delete(root,r.x,r.y);
r:=rr;
end else break;
end;
curans:=curans+dis(r.x,r.y,x,y);
insert(root,x,y);
end;

begin
readln(n,cap.x,cap.y);
readln(m);
for i:=1 to m do read(p[i].x,p[i].y);
readln(query);
fillchar(q,sizeof(q),0);
root:=0;
tot:=0;
fillchar(s,sizeof(s),0);
insert(root,0,0);
insert(root,n,0);
insert(root,cap.x,cap.y);
curans:=dis(0,0,cap.x,cap.y)+dis(n,0,cap.x,cap.y);
fillchar(flag,sizeof(flag),true);
for i:=1 to query do
begin
read(k);
if k=2 then continue;
read(q[i]);
flag[q[i]]:=false;
end;
for i:=1 to m do if flag[i] then addpoint(p[i].x,p[i].y);
fillchar(ans,sizeof(ans),0);
for i:=query downto 1 do if q[i]=0 then ans[i]:=curans else addpoint(p[q[i]].x,p[q[i]].y);
for i:=1 to query do if ans[i]<>0 then writeln(ans[i]:0:2);
end.