HAOI2011 防线修建

Posted fengxunling

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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
",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
",ans[i]);
    return 0;
}

以上是关于HAOI2011 防线修建的主要内容,如果未能解决你的问题,请参考以下文章

cogs 547:[HAOI2011] 防线修建

BZOJ2300[HAOI2011]防线修建 set维护凸包

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

bzoj2300HAOI2011防线修建

P2521 [HAOI2011]防线修建

BZOJ 2300: [HAOI2011]防线修建|set维护凸壳