bzoj1104 [POI2007]洪水pow

Description

AKD 市处在一个四面环山的谷地里。最近一场大暴雨引发了洪水, AKD 市全被水淹没了。 Blue Mary AKD 市的市长,召集了他的所有顾问(包括你)参加一个紧急会议。经过细致的商议之后,会议决定,调集若干巨型抽水机,将它们放在某些被水淹的区域,而后抽干洪水。你手头有一张 AKD 市的地图。这张地图是边长为 mn 的矩形,被划分为 mn 11 的小正方形。对于每个小正方形,地图上已经标注了它的海拔高度以及它是否是 AKD 市的一个组成部分。地图上的所有部分都被水淹没了。并且,由于这张地图描绘的地面周围都被高山所环绕,洪水不可能自动向外排出。显然,我们没有必要抽干那些非 AKD 市的区域。每个巨型抽水机可以被放在任何一个 11 正方形上。这些巨型抽水机将持续地抽水直到这个正方形区域里的水被彻底抽干为止。当然,由连通器原理,所有能向这个格子溢水的格子要么被抽干,要么水位被降低。每个格子能够向相邻的格子溢水,“相邻的”是指(在同一高度水平面上的射影)有公共边。

Input

第一行是两个数 m,n(1m,n1000) . 以下 m 行,每行 n 个数,其绝对值表示相应格子的海拔高度;若该数为正,表示他是AKD市的一个区域;否则就不是。请大家注意:所有格子的海拔高度其绝对值不超过 1000 ,且可以为零.

Output

只有一行,包含一个整数,表示至少需要放置的巨型抽水机数目。

Sample Input

6 9
-2 -2 -1 -1 -2 -2 -2 -12 -3
-2 1 -1 2 -8 -12 2 -12 -12
-5 3 1 1 -12 4 -6 2 -2
-5 -2 -2 2 -12 -3 4 -3 -1
-5 -6 -2 2 -12 5 6 2 -1
-4 -8 -8 -10 -12 -8 -6 -6 -4

Sample Output

2

Solution

bzoj 的题意果然是经常描述不清啊…
我们取出所有的城市放在 S <script type="math/tex" id="MathJax-Element-406">S</script> 集合中,从小到大枚举每个城市,从小到大排序从所有的元素中取出海拔小于等于当前城市海拔的元素,合并相邻元素,然后查询当前城市所在的联通块是否已经被覆盖,如果没有就在当前城市安上水泵。
附CE三连。
这里写图片描述

#include<bits/stdc++.h>
using namespace std;

#define N 1000008
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define INF 0x7fffffff

inline int read() {
    int x = 0, flag = 1; char ch = getchar(); while (!isdigit(ch)) { if (!(ch ^ '-')) flag = -1; ch = getchar(); }
    while (isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); return x * flag;
}

const int mv[4][2] = { 1, 0, -1, 0, 0, 1, 0 ,-1 };

struct pointType {
    int x, y, hei;
    bool operator < (const pointType& b) const { return hei < b.hei; }
}s[N], t[N];
bool tag[N];
int h[1008][1008];
int n, m, cnt, ans;

int fa[N];
int find(int x) { return (fa[x] ^ x) ? fa[x] = find(fa[x]) : x; }
int main() {
    n = read(), m = read();
    rep(i, 1, n + 1) h[i][0] = h[i][m + 1] = INF; rep(i, 1, m + 1) h[0][i] = h[n + 1][i] = INF;
    rep(i, 1, n * m) fa[i] = i;
    rep(i, 1, n) rep(j, 1, m) {
        h[i][j] = read();
        if (h[i][j] > 0) s[++cnt] = pointType{ i, j, h[i][j] };
        h[i][j] = abs(h[i][j]);
        t[(i - 1) * m + j] = pointType{ i, j, h[i][j] };
    }
    sort(s + 1, s + 1 + cnt); sort(t + 1, t + 1 + n * m);
    int j = 1;
    rep(i, 1, cnt) {
        for (; j <= m * n && t[j].hei <= s[i].hei; j++) {
            int x = t[j].x, y = t[j].y;
            rep(k, 0, 3) {
                int nx = x + mv[k][0], ny = y + mv[k][1];
                if (h[nx][ny] > h[x][y]) continue;
                int f0 = find((nx - 1) * m + ny), f1 = find((x - 1) * m + y);
                tag[f1] |= tag[f0], fa[f0] = f1;
            }
        }
        int f = find((s[i].x - 1) * m + s[i].y);
        if (!tag[f]) ans++, tag[f] = 1;
    }
    cout << ans;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值