HAOI2011 防线修建
题目链接:戳我
动态维护凸包的题目qwqwq
30分还是很好写的。。直接一个凸包就完事了
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 100010
using namespace std;
int n,nx,ny,m,q,op,top;
int nvis[MAXN];
struct Node{int x,y,id;}node[MAXN],st[MAXN];
inline bool cmp(struct Node a,struct Node b)
{
double A=atan2((a.y-node[1].y),(a.x-node[1].x));
double B=atan2((b.y-node[1].y),(b.x-node[1].x));
if(A!=B) return A<B;
return a.x<b.x;
}
inline double cross(Node a,Node b,Node c)
{
a.x-=c.x,a.y-=c.y;
b.x-=c.x,b.y-=c.y;
return a.x*b.y-a.y*b.x;
}
inline void solve()
{
node[0]=(Node){0x3f3f3f3f,0x3f3f3f3f};
int pos=0;
for(int i=1;i<=m;i++)
if(node[i].y<node[pos].y||(node[i].y==node[pos].y&&node[i].x<node[pos].x)&&!nvis[node[i].id])
pos=i;
int tmp=1;top=1;
swap(node[1],node[pos]);
sort(&node[2],&node[m+1],cmp);
while(nvis[node[tmp].id]) tmp++; st[0]=node[tmp],tmp++;
while(nvis[node[tmp].id]) tmp++; st[1]=node[tmp],tmp++;
for(int i=tmp;i<=m;i++)
{
if(nvis[node[i].id]) continue;
while(top&&cross(st[top-1],node[i],st[top])>0.0) top--;
st[++top]=node[i];
}
}
inline double dist(Node a,Node b)
{
double cur_ans=sqrt(1.0*(a.x-b.x)*(a.x-b.x)+1.0*(a.y-b.y)*(a.y-b.y));
return cur_ans;
}
inline double query()
{
double ans=0;st[top+1]=st[0];
for(int i=0;i<=top;i++) ans+=dist(st[i],st[i+1]);
return ans;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d%d%d",&n,&nx,&ny);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
int cur_x,cur_y;
scanf("%d%d",&cur_x,&cur_y);
node[i]=(Node){cur_x,cur_y,i};
}
node[++m]=(Node){0,0,m};
node[++m]=(Node){n,0,m};
node[++m]=(Node){nx,ny,m};
scanf("%d",&q);
solve();
while(q--)
{
scanf("%d",&op);
if(op==1)
{
int x;
scanf("%d",&x);
nvis[x]=1;
solve();
}
else printf("%.2lf\n",query()-n);
}
return 0;
}
然后满分做法怎么做呢?其实是一个动态凸包的模板啦!!
动态凸包只资瓷加点,所以我们把所有的询问先离线下来,然后倒着做。
排序方法有两种,一种是水平的,一种是极角的。(水平序听说维护起来很麻烦的样子)这里采用的是极角序,当极角相同的时候,按照它和凸包的中心点的远近来排序。
因为题目中已经给出了三个点,所以这个中心点就是这个三角形的中心啦!
我们往里面加点的时候,设nxt为第一个极角比x大的,pre为第一个极角比x小的。这里的前驱后继寻找肯定不能O(n)了,我们采用logn的数据结构平衡树!!!set。
然后如果这个x点已经在凸包里面了,我们就可以return了,不需要任何其他操作了。
但是如果这个点在凸包的外面,显然要把pre和nxt之间的边断掉,加入pre到x和nxt到x的边。
然后还需要考虑两种情况,一种是极角比pre小的一些点是否在x加进来之后成为了凹进去的点(也就是夹角大于180度)?对应的另一种情况是极角nxt大的点。
所以我们还要用两个while来进行判断。
整体时间复杂度上限为\(O(nlogn)\)。
因为蒟蒻不会做动态凸包,所以思路上很大程度上参考自这位大佬,在此表示深深的感激qwqwq。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
#define eps 1e-6
#define MAXN 100010
using namespace std;
int n,x,y,m,q,tot;
bool del[MAXN];
double cur_ans;
double ans[MAXN];
struct Node{
double x,y,ang,len;
Node(double x=0,double y=0,double ang=0,double len=0):
x(x),y(y),ang(ang),len(len){}
}a[MAXN],o;
struct Que{int id,op;}que[MAXN];
set<Node>st;
set<Node>::iterator nxt,pre,tmp;
inline bool operator < (const Node &a,const Node &b)
{
if(fabs(a.ang-b.ang)<eps) return a.len<b.len;
return a.ang<b.ang;
}
inline Node operator - (const Node a,const Node b){return (Node){a.x-b.x,a.y-b.y};}
inline double dot(Node a,Node b){return a.x*b.x+a.y*b.y;}
inline double cross(Node a,Node b){return a.x*b.y-a.y*b.x;}
inline double get_len(Node a){return sqrt(dot(a,a));}
inline set<Node>::iterator get_pre(set<Node>::iterator it)
{
if(it==st.begin()) it=st.end();
it--;
return it;
}
inline set<Node>::iterator get_nxt(set<Node>::iterator it)
{
it++;
if(it==st.end()) it=st.begin();
return it;
}
inline void solve(Node x)
{
nxt=st.lower_bound(x);
if(nxt==st.end()) nxt=st.begin();
pre=get_pre(nxt);
if(cross((*nxt)-(*pre),x-(*pre))>=0) return;
cur_ans-=get_len((*nxt)-(*pre));
cur_ans+=get_len(x-(*nxt))+get_len(x-(*pre));
st.insert(x);
tmp=get_pre(pre);
while(cross(x-(*pre),(*pre)-(*tmp))>=0)
{
cur_ans-=get_len(x-(*pre))+get_len((*pre)-(*tmp));
cur_ans+=get_len(x-(*tmp));
st.erase(pre);
pre=tmp;
tmp=get_pre(pre);
}
tmp=get_nxt(nxt);
while(cross(x-(*nxt),(*nxt)-(*tmp))<=0)
{
cur_ans-=get_len(x-(*nxt))+get_len((*nxt)-(*tmp));
cur_ans+=get_len(x-(*tmp));
st.erase(nxt);
nxt=tmp;
tmp=get_nxt(nxt);
}
return;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d%d%d",&n,&x,&y);
a[0]=(Node){0,0};
a[1]=(Node){n,0},o.x+=n;
a[2]=(Node){x,y},o.x+=x,o.y+=y;
cur_ans+=get_len(a[2]-a[0])+get_len(a[2]-a[1]);
o.x/=3,o.y/=3;
for(int i=0;i<=2;i++)
{
a[i].ang=atan2(a[i].y-o.y,a[i].x-o.x);
a[i].len=get_len(a[i]-o);
st.insert(a[i]);
}
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%lf%lf",&a[i].x,&a[i].y);
a[i].ang=atan2(a[i].y-o.y,a[i].x-o.x);
a[i].len=get_len(a[i]-o);
}
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
scanf("%d",&que[i].op);
if(que[i].op==1) scanf("%d",&que[i].id),del[que[i].id]=true;
}
for(int i=1;i<=m;i++)
if(del[i]==false)
solve(a[i]);
for(int i=q;i>=1;i--)
{
if(que[i].op==2) ans[++tot]=cur_ans;
else solve(a[que[i].id]);
}
for(int i=tot;i;i--) printf("%.2lf\n",ans[i]);
return 0;
}