zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    Linux环境下为普通用户添加sudo权限
    【转】在 Ubuntu 中使用 NTP 进行时间同步
    Shell脚本:批量添加用户,并设置随机字符为密码
    【转】Linux目录结构和常用命令
    系统启动流程
    linux系统利用libudev获取USB设备的VID和PID
    cJSON详解
    ajax读取文件内容
    window.location网页URL信息
    html实现网站全局按钮点击后置灰,不允许连续点击
  • 原文地址:https://www.cnblogs.com/fengxunling/p/10459598.html
Copyright © 2011-2022 走看看