[BZOJ 1804] Flood
Link:
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:
遇到平面图时(保证边两两不相交),一定要考虑其对偶图