zoukankan      html  css  js  c++  java
  • [BZOJ 1804] Flood

    Link:

    BZOJ 1804 传送门

    Solution:

    不容易啊,第一道完全自己A掉的IOI题目.....

    算法思想其实很简单:

    模拟缩减的过程即可

    将每条边转为2条有向边,每次找到最左边的边,沿着最外圈走一周,并将走过的边打上$vis$标记

    最后那些正向和反向都被走过的边就是能留下的边(最后形成链的边)

    难点在于如何实现在最外层走一周:

    由于这是一个平面图,我们对每个点只要记录上下左右四个方向的边即可

    保证是顺时针转,因此尽量往左边走就能满足最外圈这个条件

    (注意对$vis$数组更新的位置,否则可能在遇到链时死循环)

    其实还有一种不带$log$的做法:

    平面图转化为对偶图,$bfs$每条边到边界的距离

    如果两边的距离相等,这条边就计入答案(难点在建图)

    待填坑

    Code:

    #include <bits/stdc++.h>
     
    using namespace std;
    typedef pair<int,int> P;
    #define X first
    #define Y second
    const int MAXN=4e5+10;
     
    P nd[MAXN],tp[MAXN];
    int n,m,dir[MAXN][4],cnt[MAXN][4],vis[MAXN][4],res=0;
    int tot=0,tdir[MAXN];
    struct edge{int x,y,dir;} e[2*MAXN];
    bool cmp(edge a,edge b) //对边的排序
    {
        if(nd[a.x].X==nd[b.x].X)
            if((a.dir==0 || a.dir==2) && (b.dir==1 || b.dir==3)) return true;
            else if((a.dir==1 || a.dir==3) && (b.dir==0 || b.dir==2)) return false;
        return nd[a.x].X<nd[b.x].X;
    }
     
    void add_edge(int a,int b,int id)
    {
        if(nd[a].X==nd[b].X)
            if(nd[a].Y<nd[b].Y) dir[a][0]=b,dir[b][2]=a,e[id].dir=0;
            else dir[a][2]=b,dir[b][0]=a,e[id].dir=2;
        else
            if(nd[a].X<nd[b].X) dir[a][1]=b,dir[b][3]=a,e[id].dir=1;
            else dir[a][3]=b,dir[b][1]=a,e[id].dir=3;;
    }
     
    int Back(int x){return (x+2)%4;}
    int Left(int x){return (x+3)%4;}
    int Right(int x){return (x+1)%4;}
    void Travel(int id)
    {
        int cur=e[id].y,d=e[id].dir,pre=e[id].x;cnt[pre][d]++;
        tot=1;tp[tot]=P(pre,cur);tdir[tot]=d;
        while(cur!=e[id].x)
        {        
            d=Left(d); //尽量往左走
            while(!dir[cur][d] || vis[cur][d]) d=Right(d);
            pre=cur;cur=dir[cur][d];cnt[pre][d]++;
            tp[++tot]=P(pre,cur);tdir[tot]=d;
        }
        for(int i=1;i<=tot;i++) //一定要最后一起打vis标记,否则可能死循环
            vis[tp[i].X][tdir[i]]=vis[tp[i].Y][Back(tdir[i])]=true;
    }
     
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d%d",&nd[i].X,&nd[i].Y);
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&e[i].x,&e[i].y);
            if(nd[e[i].x].X>nd[e[i].y].X) swap(e[i].x,e[i].y);
            if(nd[e[i].x].Y>nd[e[i].y].Y) swap(e[i].x,e[i].y);
            add_edge(e[i].x,e[i].y,i);
        }
        
        sort(e+1,e+m+1,cmp);
        for(int i=1;i<=m;i++)
        {
            if(vis[e[i].x][e[i].dir]) continue; //已走过的边
            Travel(i);
        }
        for(int i=1;i<=m;i++)
            if(cnt[e[i].x][e[i].dir] && cnt[e[i].y][Back(e[i].dir)]) res++;
        printf("%d",res);
        return 0;
    }

    Review:

    遇到平面图时(保证边两两不相交),一定要考虑其对偶图

  • 相关阅读:
    JavaScript:综合案例---房贷计算器的实现
    iOS:如何将自己的SDK用CocoaPods管理
    JavaScript:综合案例-表单验证
    JavaScript:日期选择器组件的使用
    JavaScript : 基本的处理事件
    JavaScript:window窗口对象
    JavaScript:文本域事件处理
    JavaScript:下拉列表框的事件处理
    JavaScript:复选框事件的处理
    JavaScript:单选钮的事件处理
  • 原文地址:https://www.cnblogs.com/newera/p/9141827.html
Copyright © 2011-2022 走看看