@noi.ac - 491@ explore
@description@
最近有一个巨大的古代地下遗迹在比特镇被发现。这个地下遗迹的俯视图由 n 行 m 列共 n×m 个格子组成,每个格子表示一个房间,两个房间相邻当且仅当它们存在公共边。经过勘测,这个地下遗迹中有 k 个房间发生了塌陷,这些房间是不可通行的。
为了深入探索这个古代遗迹,考古队需要选择一个没有发生塌陷的房间,从地面上径直打一口井下去,然后探索从该房间能到达的所有房间。
因为打井非常耗费时间和金钱,请写一个程序帮助考古队计算至少需要打多少口井,才能将所有没有发生塌陷的房间都探索一遍。
input
第一行包含三个正整数 n,m,k,分别表示遗迹俯视图的长度、宽度以及塌陷房间的数量。
接下来 k 行,每行两个正整数 xi,yi,表示第 i 个塌陷房间的位置。
output
输出一行一个整数,即需要打的井的数量。
sample input
2 3 2
1 2
2 1
sample output
2
对于 100% 的数据:
1≤xi≤n≤10^9, 1≤yi≤m≤10^9, k<n×m且k≤100000。保证同一个房间最多只会被描述一次。
@solution@
简单来说:一个白色 n*m 棋盘,将其中 k 个格子涂黑,求最终白色四连通块数量。
先离散化,将 n, m 的大小缩小到 10^6 的数量级。
注意离散化时要将一个格子四周的点(常数*3)都要进行离散化,否则会出现原本不相邻的格子离散化后就相邻的情况。
为了方便处理,我们在棋盘的边框外再添加黑格将棋盘包围起来(常数++)。
可以得到一个白色四连通块总是被某个黑色八连通块(注意不是黑色四连通块)包围着。
进一步地,与包围的黑色八连通块相邻的白格决定了这个白色连通块。
所以我们可以只提取与黑格八连通(注意这里也不是四连通)的白格(常数*8)。
(zxb 大佬形象地将其描述为“描边法”)
得到一个粗略的算法:提取一个黑色八连通块,找到与这些黑格八连通的白格,寻找这些白格构成了多少四连通块。
但是有一个小小的 bug:我们提取出的白色四连通块,可能不满足黑连通块包围白连通块,而是反过来白连通块包围黑连通块,这样就会产生重复计数。
但是观察到黑连通块至多只被一个白连通块包围。且如果我们在棋盘外的黑格的外面再添加四连通的白格将棋盘外的黑格包围起来(常数++),每个黑连通块恰好会被一个白连通块包围。
于是就可以用白连通块个数 - 黑连通块个数得到正确答案。
注意找连通块时可能需要 map、lower_bound、hash 等帮助你确定某个棋格是黑格还是白格。
一个 O(nlog n)(log n的瓶颈卡在离散化上)的大常数算法。
@accepted code@
#include<queue>
#include<cstdio>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define lb lower_bound
typedef pair<int, int> pii;
struct node{
pii p; bool vis;
node(pii _p=mp(0, 0), bool _v=false):p(_p), vis(_v){}
friend bool operator < (node a, node b) {return a.p < b.p;}
friend bool operator == (node a, node b) {return a.p == b.p;}
};
vector<node>b, w;
vector<int>vx, vy;
int main() {
int n, m, k; scanf("%d%d%d", &n, &m, &k);
vx.clear(), vx.pb(0), vx.pb(1), vx.pb(n), vx.pb(n + 1);
vy.clear(), vy.pb(0), vy.pb(1), vy.pb(m), vy.pb(m + 1);
for(int i=1;i<=k;i++) {
int x, y; scanf("%d%d", &x, &y);
b.pb(node(mp(x, y)));
vx.pb(x - 1), vx.pb(x), vx.pb(x + 1);
vy.pb(y - 1), vy.pb(y), vy.pb(y + 1);
}
sort(vx.begin(), vx.end()), vx.erase(unique(vx.begin(), vx.end()), vx.end());
sort(vy.begin(), vy.end()), vy.erase(unique(vy.begin(), vy.end()), vy.end());
for(int i=0;i<k;i++) {
b[i].p.fi = lb(vx.begin(), vx.end(), b[i].p.fi) - vx.begin();
b[i].p.se = lb(vy.begin(), vy.end(), b[i].p.se) - vy.begin();
}
for(int i=1;i<vx.size()-1;i++)
b.pb(node(mp(i, 0))), b.pb(node(mp(i, vy.size()-1)));
for(int i=1;i<vy.size()-1;i++)
b.pb(node(mp(0, i))), b.pb(node(mp(vx.size()-1, i)));
sort(b.begin(), b.end()), b.erase(unique(b.begin(), b.end()), b.end());
int ans = 0;
for(int i=0;i<b.size();i++) {
if( b[i].vis ) continue;
b[i].vis = true; w.clear();
queue<pii>que; que.push(b[i].p);
while( !que.empty() ) {
pii f = que.front(); que.pop();
for(int dx=-1;dx<=1;dx++)
for(int dy=-1;dy<=1;dy++) {
pii p = mp(f.fi + dx, f.se + dy);
int x = lb(b.begin(), b.end(), node(p)) - b.begin();
if( x != b.size() && b[x].p == p ) {
if( !b[x].vis )
b[x].vis = true, que.push(p);
}
else w.pb(p);
}
}
sort(w.begin(), w.end()), w.erase(unique(w.begin(), w.end()), w.end());
for(int j=0;j<w.size();j++) {
if( w[j].vis ) continue;
ans++; w[j].vis = true; que.push(w[j].p);
while( !que.empty() ) {
pii f = que.front(); que.pop();
for(int dx=-1;dx<=1;dx++)
for(int dy=-1;dy<=1;dy++) {
if( dx && dy ) continue;
pii p = mp(f.fi + dx, f.se + dy);
int x = lb(w.begin(), w.end(), node(p)) - w.begin();
if( x != w.size() && w[x].p == p && !w[x].vis )
w[x].vis = true, que.push(p);
}
}
}
ans--;
}
printf("%d\n", ans);
}
@details@
康复计划 - 3。
“我就不信即使我到处都在用大常数 STL,一个 O(nlog n) 的算法跑不过 10^5!”
这是我 TLE 之前的心理活动。
把 map 换成 lower_bound 查找就过了。
人太菜了,写的代码太丑了,评测机只能把大常数 O(nlog n) 当成 O(n^2) 跑。。。
代码非常ACM(指使用define进行缩写)
@noi.ac - 491@ explore的更多相关文章
- # NOI.AC省选赛 第五场T1 子集,与&最大值
NOI.AC省选赛 第五场T1 A. Mas的童年 题目链接 http://noi.ac/problem/309 思路 0x00 \(n^2\)的暴力挺简单的. ans=max(ans,xor[j-1 ...
- NOI.ac #31 MST DP、哈希
题目传送门:http://noi.ac/problem/31 一道思路好题考虑模拟$Kruskal$的加边方式,然后能够发现非最小生成树边只能在一个已经由边权更小的边连成的连通块中,而树边一定会让两个 ...
- NOI.AC NOIP模拟赛 第五场 游记
NOI.AC NOIP模拟赛 第五场 游记 count 题目大意: 长度为\(n+1(n\le10^5)\)的序列\(A\),其中的每个数都是不大于\(n\)的正整数,且\(n\)以内每个正整数至少出 ...
- NOI.AC NOIP模拟赛 第六场 游记
NOI.AC NOIP模拟赛 第六场 游记 queen 题目大意: 在一个\(n\times n(n\le10^5)\)的棋盘上,放有\(m(m\le10^5)\)个皇后,其中每一个皇后都可以向上.下 ...
- NOI.AC NOIP模拟赛 第二场 补记
NOI.AC NOIP模拟赛 第二场 补记 palindrome 题目大意: 同[CEOI2017]Palindromic Partitions string 同[TC11326]Impossible ...
- NOI.AC NOIP模拟赛 第一场 补记
NOI.AC NOIP模拟赛 第一场 补记 candy 题目大意: 有两个超市,每个超市有\(n(n\le10^5)\)个糖,每个糖\(W\)元.每颗糖有一个愉悦度,其中,第一家商店中的第\(i\)颗 ...
- NOI.AC NOIP模拟赛 第四场 补记
NOI.AC NOIP模拟赛 第四场 补记 子图 题目大意: 一张\(n(n\le5\times10^5)\)个点,\(m(m\le5\times10^5)\)条边的无向图.删去第\(i\)条边需要\ ...
- NOI.AC NOIP模拟赛 第三场 补记
NOI.AC NOIP模拟赛 第三场 补记 列队 题目大意: 给定一个\(n\times m(n,m\le1000)\)的矩阵,每个格子上有一个数\(w_{i,j}\).保证\(w_{i,j}\)互不 ...
- NOI.AC WC模拟赛
4C(容斥) http://noi.ac/contest/56/problem/25 同时交换一行或一列对答案显然没有影响,于是将行列均从大到小排序,每次处理限制相同的一段行列(呈一个L形). 问题变 ...
随机推荐
- SpringMVC返回json的问题
在使用springmvc的时候,如果返回值是String, 返回一个json的字符串,在js里面接收会有问题,不能直接当成json使用,要通过eval来转成json. 就像你在js里面直接定义 var ...
- 第十章—DOM(二)——Element类型
Element类型用于表现HTML和XML,提供了对元素标签名,子节点和特效的访问.Element节点具有以下特征: 要访问元素的标签名,可以使用nodeName属性,也可以使用tagName属性.这 ...
- Pycharm安装package报错:AttributeError: module 'pip' has no attribute 'main'
Pycharm安装package报错:AttributeError: module 'pip' has no attribute 'main' 确认pip已经升级到目前最新版本了. 在网上搜寻后,解决 ...
- Python多线程在爬虫中的应用
题记:作为测试工程师经常需要解决测试数据来源的问题,解决思路无非是三种:(1)直接从生产环境拷贝真实数据 (2)从互联网上爬取数据 (3)自己用脚本或者工具造数据.前段时间,为了获取更多的测试数据,笔 ...
- JAVA的一次编译,到处执行,你知道多少?
版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/wangyongxia921/article/details/28117155 一.对AVA的迷茫 ...
- 设置程序PrivatePath,配置引用程序集的路径(分离exe和dll)
原文:设置程序PrivatePath,配置引用程序集的路径(分离exe和dll) 有时候我们想让程序的exe文件和dll文件分开在不同目录,这时候可以有3种方法 1.在app.config中配置 &l ...
- docker.[6] 数据卷
docker.[6] 数据卷 操作指令: # docker run -v /data1:/data2 -i -t centos /bin/bash 参数说明: data1 : 这里指的是宿主机的目录( ...
- 实现移动端上拉加载和下拉刷新的vue插件(mescroll.js)
做一个简单的移动端展示项目,后台分页后前端加载,实现上拉加载下一页,找了下,还是用这个mescroll.js插件好一点 1.npm安装 npm install --save mescroll.js / ...
- java 日期合法
try { String date_str = "5555-22-33"; SimpleDateFormat format=new SimpleDateFormat("y ...
- system_service进程里 调用SystemManager.getService("activity") 直接返回ams的引用?
我们知道ActivityManager是运行在system_service进程里的,但是最近看代码发现在这个进程的其他服务线程里为了获取AMS调用: ActivityManagerService am ...