@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的更多相关文章

  1. # NOI.AC省选赛 第五场T1 子集,与&最大值

    NOI.AC省选赛 第五场T1 A. Mas的童年 题目链接 http://noi.ac/problem/309 思路 0x00 \(n^2\)的暴力挺简单的. ans=max(ans,xor[j-1 ...

  2. NOI.ac #31 MST DP、哈希

    题目传送门:http://noi.ac/problem/31 一道思路好题考虑模拟$Kruskal$的加边方式,然后能够发现非最小生成树边只能在一个已经由边权更小的边连成的连通块中,而树边一定会让两个 ...

  3. NOI.AC NOIP模拟赛 第五场 游记

    NOI.AC NOIP模拟赛 第五场 游记 count 题目大意: 长度为\(n+1(n\le10^5)\)的序列\(A\),其中的每个数都是不大于\(n\)的正整数,且\(n\)以内每个正整数至少出 ...

  4. NOI.AC NOIP模拟赛 第六场 游记

    NOI.AC NOIP模拟赛 第六场 游记 queen 题目大意: 在一个\(n\times n(n\le10^5)\)的棋盘上,放有\(m(m\le10^5)\)个皇后,其中每一个皇后都可以向上.下 ...

  5. NOI.AC NOIP模拟赛 第二场 补记

    NOI.AC NOIP模拟赛 第二场 补记 palindrome 题目大意: 同[CEOI2017]Palindromic Partitions string 同[TC11326]Impossible ...

  6. NOI.AC NOIP模拟赛 第一场 补记

    NOI.AC NOIP模拟赛 第一场 补记 candy 题目大意: 有两个超市,每个超市有\(n(n\le10^5)\)个糖,每个糖\(W\)元.每颗糖有一个愉悦度,其中,第一家商店中的第\(i\)颗 ...

  7. NOI.AC NOIP模拟赛 第四场 补记

    NOI.AC NOIP模拟赛 第四场 补记 子图 题目大意: 一张\(n(n\le5\times10^5)\)个点,\(m(m\le5\times10^5)\)条边的无向图.删去第\(i\)条边需要\ ...

  8. NOI.AC NOIP模拟赛 第三场 补记

    NOI.AC NOIP模拟赛 第三场 补记 列队 题目大意: 给定一个\(n\times m(n,m\le1000)\)的矩阵,每个格子上有一个数\(w_{i,j}\).保证\(w_{i,j}\)互不 ...

  9. NOI.AC WC模拟赛

    4C(容斥) http://noi.ac/contest/56/problem/25 同时交换一行或一列对答案显然没有影响,于是将行列均从大到小排序,每次处理限制相同的一段行列(呈一个L形). 问题变 ...

随机推荐

  1. Redis分布式锁的实现及注意事项

    一.前言 分布式锁一般有三种实现方式: 1. 数据库乐观锁: 2. 基于Redis的分布式锁: 3. 基于ZooKeeper的分布式锁. 本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上 ...

  2. ListView设置的点点滴滴

    去掉ListView的分界线 1. ListView的属性Divider设为#FFCC00      这种对任何背景都适用 2. 把ListView的属性Divider设为和背景一样的颜色 3.and ...

  3. GDOI2017第四轮day1总结

    总的来说这场比赛,只能说是勉强正常发挥. 实在是知识水平有限,最后没能突破瓶颈. 有几个做得好的地方: 1.想好了在写题: 2.暴力也会拍 3.适当地放弃题. 要学习的东西: 1.Sg,线性基: 2. ...

  4. Bundler和Minifier Visual Studio扩展

    原文地址:https://marketplace.visualstudio.com/items?itemName=MadsKristensen.BundlerMinifier 特征 将CSS,Java ...

  5. php表单传值--GET和POST

    一.       传值 1.    传值/接收方法: 1)        GET(5种方式!) a)       表单Form: method = ‘get’   GET接收数据方式: b)      ...

  6. 大数据技术之Hadoop入门

      第1章 大数据概论 1.1 大数据概念 大数据概念如图2-1 所示. 图2-1 大数据概念 1.2 大数据特点(4V) 大数据特点如图2-2,2-3,2-4,2-5所示 图2-2 大数据特点之大量 ...

  7. QT UI 线程为什么卡死?

    我的工程是由三个线程处理不同任务构成的,其中UI用于显示,还有数据处理和数据接收发送线程. 在运行的过程中发现由于数据处理线程不及时,超过了设定的100ms,导致UI卡死,几个周期后又恢复,接着又卡死 ...

  8. Word画线条5大技巧,简单实用!

    [Word画线条5大技巧,简单实用!]1.输入三个“=”,回车,就是一条双直线:2.输入三个“~”,回车,就是一条波浪线:3.输入三个“”回车,就是一条虚线:4.输入三个“-”,回车,就是一条直线:5 ...

  9. pycharm 永久注册

    pycharm 使用又到期了,找到了破解版亲测(到期日期2099/12/31),绝对简单好用,直接使用步骤: 一,下载pycharm(windows版):  https://www.jetbrains ...

  10. Selenium-Switch与SelectApi接口

    Switch 我们在UI自动化测试时,总会出现新建一个tab页面.弹出一个浏览器级别的弹框或者是出现一个iframe标签,这时我们用WebDriver提供的Api接口就无法处理这些情况了.需要用到Se ...