题面传送门

一道挺有意思的题(?)

orz djq yyds %%%%%%%%%%%%%%%%%%

首先一个很直观的想法是将每个房间看作一个节点,在有墙的房间旁边连边权为 \(1\) 的边然后 bfs,不过这样实现不太方便,最终也无法较直接地判断每堵墙是否被摧毁,因此这里(djq)提供了一个比较简单的方法。

我们将每堵墙拆成两个点 \(i\) 和 \(i+m\) 分别表示这堵墙分隔的两个区域,我们在这两个点之间连边权为 \(1\) 的边,而显然有的区域表示的是同一个房间,因此我们还要在它们之间连边权为 \(0\) 的边。具体来说,对于每个点而言会连出一些边,假设个数为 \(c\),那么这个点引出的这些边就将平面分成了 \(c\) 个部分,而这些边总共对应着 \(2c\) 个点,因此总共会有 \(c\) 对点表示的是同一个区域,而这些表示相同区域的点表示的射线显然是相邻的,因此我们对于每个点开一个 vector 按顺序记录它连出的边的编号,然后扫一遍在相邻的边表示的区域之间连边,并根据方向判断是 \(i\) 还是 \(i+m\),这样即可实现缩点(u1s1 这一段可能讲得不是特别明白,具体看代码罢)

缩完点之后找出表示外层区域的点,这个可以暴力通过对于每个区域进行一遍 DFS 找出纵坐标最大(当然横坐标也行)的边,那么它上方的区域表示的点就是表示最外侧区域的点,由于此题边权只涉及 \(0/1\),因此进行 01 bfs 即可求出到每个区域的最短距离。

时间复杂度线性。

const int MAXN=1e5;
const int MAXM=2e5;
const int MAXV=4e5;
const int MAXE=2e6;
const int dx[]={1,0,-1,0};
const int dy[]={0,1,0,-1};
int n,m,x[MAXN+5],y[MAXN+5],id[MAXN+5][4],dr[MAXN+5][4];
int sgn(int x){return (x>0)?1:((!x)?0:-1);}
int direc(int a,int b){for(int i=0;i<4;i++) if(sgn(x[b]-x[a])==dx[i]&&sgn(y[b]-y[a])==dy[i]) return i;}
int hd[MAXV+5],to[MAXE+5],nxt[MAXE+5],val[MAXE+5],ec=0;
void adde(int u,int v,int w){/*printf("%d %d %d\n",u,v,w);*/to[++ec]=v;val[ec]=w;nxt[ec]=hd[u];hd[u]=ec;}
int my,ids;bitset<MAXN+5> vis;
void dfs(int t){
if(vis[t]) return;vis.set(t);for(int i=0;i<4;i++) if(id[t][i]){
dfs(dr[t][i]);if((i==0||i==2)&&y[t]>my) my=y[t],ids=id[t][i]+m;
}
}
int dis[MAXV+5];
void bfs(int s){
deque<int> q;dis[s]=0;q.push_back(s);
while(!q.empty()){
int x=q.front();q.pop_front();//printf("%d\n",x);
for(int e=hd[x];e;e=nxt[e]){
// printf("edge %d\n",e);
int y=to[e],z=val[e];
// printf("%d %d\n",y,z);
if(dis[y]>dis[x]+z){
dis[y]=dis[x]+z;
(!z)?(q.push_front(y)):(q.push_back(y));
}
}
}
// for(int i=1;i<=m<<1;i++) printf("%d %d\n",i,dis[i]);
}
int main(){
scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]);
scanf("%d",&m);for(int i=1;i<=m;i++){
int a,b;scanf("%d%d",&a,&b);
// printf("%d %d\n",direc(a,b),direc(b,a));
id[a][direc(a,b)]=id[b][direc(b,a)]=i;
dr[a][direc(a,b)]=b;dr[b][direc(b,a)]=a;
adde(i,i+m,1);adde(i+m,i,1);
} memset(dis,63,sizeof(dis));
for(int i=1;i<=n;i++){
// printf("Vert %d\n",i);
vector<int> d;
for(int j=0;j<4;j++) (id[i][j]&&(d.pb(j),0))/*,printf("%d %d\n",j,id[i][j])*/;
if(!d.empty()){
d.pb(d[0]);//printf("%d\n",d.size());
for(int j=0;j+1<d.size();j++){
int a=d[j],b=d[j+1];
// printf("%d %d\n",id[i][a],id[i][b]);
adde(id[i][a]+m*(a==0||a==3),id[i][b]+m*(b==1||b==2),0);
adde(id[i][b]+m*(b==1||b==2),id[i][a]+m*(a==0||a==3),0);
}
}
} vector<int> ans;
for(int i=1;i<=n;i++) if(!vis[i]){my=-1;ids=0;dfs(i);bfs(ids);}
for(int i=1;i<=m;i++) if(dis[i]==dis[i+m]) ans.pb(i);
printf("%d\n",ans.size());for(int i=0;i<ans.size();i++) printf("%d\n",ans[i]);
return 0;
}

洛谷 P4646 - [IOI2007] flood 洪水(拆点+bfs)的更多相关文章

  1. 【题解】洛谷P1032 [NOIP2002TG]字串变换(BFS+字符串)

    洛谷P1032:https://www.luogu.org/problemnew/show/P1032 思路 初看题目觉得挺简单的一道题 但是仔细想了一下发现实现代码挺麻烦的 而且2002年的毒瘤输入 ...

  2. 洛谷 2831 (NOIp2016) 愤怒的小鸟——仅+1所以bfs优化

    题目:https://www.luogu.org/problemnew/show/P2831 状压dp.跑得很慢.(n^2*2^n) 注意只打一只猪的情况. #include<iostream& ...

  3. BZOJ1804: [Ioi2007]Flood 洪水

    把点按坐标排序,每次找出最小的点,一定在最外层,再顺着把最外层的边删掉,经过了两次的边不会被冲毁. 其实不难写,但是写了很久. #include<bits/stdc++.h> #defin ...

  4. 洛谷 P4401 [IOI2007]Miners 矿工配餐

    题意简述 有两个矿洞,已知食物的种类(≤3)和顺序,将他们送往任一矿洞, 若一个矿洞3次食物相同,贡献1:若有2种不同食物,贡献2:若有3种不同食物,贡献3 求最大贡献 题解思路 food[i] 为当 ...

  5. 【洛谷P4289】移动玩具 状压bfs

    代码如下 #include <bits/stdc++.h> using namespace std; const int dx[]={0,0,1,-1}; const int dy[]={ ...

  6. 洛谷 P1135 奇怪的电梯 【基础BFS】

    题目链接:https://www.luogu.org/problemnew/show/P1135 题目描述 呵呵,有一天我做了一个梦,梦见了一种很奇怪的电梯.大楼的每一层楼都可以停电梯,而且第 i 层 ...

  7. 【洛谷】P1379 八数码难题(bfs)

    题目 题目描述 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要求解的问题是:给出一种初始布局(初始状态)和目标布局 ...

  8. 洛谷 P2578 [ZJOI2005]九数码游戏【bfs+康托展开】

    只有9!=362880个状态,用康托展开hash一下直接bfs即可 #include<iostream> #include<cstdio> #include<cstrin ...

  9. 洛谷 P1379 八数码难题(map && 双向bfs)

    题目传送门 解题思路: 一道bfs,本题最难的一点就是如何储存已经被访问过的状态,如果直接开一个bool数组,空间肯定会炸,所以我们要用另一个数据结构存,STL大法好,用map来存,直接AC. AC代 ...

随机推荐

  1. 面试题 08.12. N皇后

    题目 设计一种算法,打印 N 皇后在 N × N 棋盘上的各种摆法,其中每个皇后都不同行.不同列,也不在对角线上.这里的"对角线"指的是所有的对角线,不只是平分整个棋盘的那两条对角 ...

  2. vue3.x全局$toast、$message、$loading等js插件

    有时候我们需要使用一些类似toast,messge.loading这些跟js交互很频繁的插件,vue3.x这类插件的定义跟vue2.x插件稍大,而且相对变得复杂了一点点. 第一种.需要时创建,用完移除 ...

  3. [Beta]the Agiles Scrum Meeting 5

    会议时间:2020.5.19 20:00 1.每个人的工作 今天已完成的工作 成员 已完成的工作 issue yjy 为评测机增加更多评测指标 评测部分增加更多评测指标 tq 为评测机增加更多评测指标 ...

  4. [no_code团队]项目介绍 & 需求分析 & 发布预测

    项目 内容 2020春季计算机学院软件工程(罗杰 任健) 博客园班级博客 作业要求 团队项目选择 我们在这个课程的目标是 在团队合作中提升软件开发水平 这个作业在哪个具体方面帮助我们实现目标 进行项目 ...

  5. java中延时队列的使用

    最近遇到这么一个需求,程序中有一个功能需要发送短信,当满足某些条件后,如果上一步的短信还没有发送出去,那么应该取消这个短信的发送.在翻阅java的api后,发现java中有一个延时队列可以解决这个问题 ...

  6. 零基础学习Linux心得总结

    很多同学接触linux不多,对linux平台的开发更是一无所知. 而现在的趋势越来越表明,作为一个优秀的软件开发人员,或计算机it行业从业人员,="" 掌握linux是一种很重要的 ...

  7. 在c中使用正则表达式

    今天学习编译原理的时候,用c写一个简易的文法识别器实验遇到了一个问题:要用正则表达式去识别正则文法里面的A->ω,A->Bω, 其中ω属于T的正闭包,也就是说我们对正则文法的产生式进行抽象 ...

  8. hdu 5057 Argestes and Sequence (数状数组+离线处理)

    题意: 给N个数.a[1]....a[N]. M种操作: S X Y:令a[X]=Y Q L R D P:查询a[L]...a[R]中满足第D位上数字为P的数的个数 数据范围: 1<=T< ...

  9. Python技法4:闭包

    闭包:用函数代替类 有时我们会定义只有一个方法(除了__init__()之外)的类,而这种类可以通过使用闭包(closure)来替代.闭包是被外层函数包围的内层函数,它能够获取外层函数范围中的变量(即 ...

  10. CobaltStrike上线Linux

    为获得最佳的阅读体验,请访问我的个人主页: https://xzajyjs.cn/ 在红蓝对抗中,我们常需要对目标进行长时间的控制,cobaltstrike原生对于上线windows比较轻松友好,但如 ...