bzoj 2936 [Poi 1999] 降水 - 并查集
考虑算每一高度能储存的水的量。
如果小于等于这个高度的格子和边界连通,那么水就会流走,这一部分不能算入答案。
所以用并查集维护高度小于等于当前高度的格子的连通性。每次答案加已经找到的格子数目减去和边界连通的格子数。
时间复杂度$O(nm + V)$。(用真·并查集就是这样)
网上咋一群带个log的做法。想去loj出个加强版。
Code
1 /** 2 * bzoj 3 * Problem#2936 4 * Accepted 5 * Time: 56ms 6 * Memory: 1688k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 const int N = 105, V = 10000; 13 const int mov[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; 14 15 typedef class Dsu { 16 public: 17 int *f; 18 int *siz; 19 20 Dsu() { } 21 Dsu(int n) { 22 f = new int[(n + 1)]; 23 siz = new int[(n + 1)]; 24 f[0] = siz[0] = 0; 25 for (int i = 1; i <= n; i++) 26 f[i] = i; 27 for (int i = 1; i <= n; i++) 28 siz[i] = 1; 29 } 30 31 int find(int x) { 32 return (f[x] == x) ? (x) :(f[x] = find(f[x])); 33 } 34 35 void unit(int x, int y) { 36 int fx = find(x); 37 int fy = find(y); 38 if (fx == fy) 39 return; 40 siz[fy] += siz[fx]; 41 f[fx] = fy; 42 } 43 }Dsu; 44 45 int n, m; 46 Dsu uf; 47 boolean found[N][N]; 48 vector< pair<int, int> > vs[V + 1]; 49 50 int id(int x, int y) { 51 if (!x || !y || x == n + 1 || y == m + 1) 52 return 0; 53 return (x - 1) * m + y; 54 } 55 56 inline void init() { 57 scanf("%d%d", &n, &m); 58 uf = Dsu(n * m); 59 for (int i = 1; i <= n; i++) 60 for (int j = 1, h; j <= m; j++) { 61 scanf("%d", &h); 62 vs[h].push_back(pair<int, int>(i, j)); 63 } 64 } 65 66 int cfound = 0, res = 0; 67 inline void solve() { 68 int all = n * m; 69 for (int v = 1; v <= V && uf.siz[uf.find(0)] != all; v++) { 70 for (int j = 0; j < (signed) vs[v].size(); j++) { 71 int x = vs[v][j].first, y = vs[v][j].second; 72 found[x][y] = true, cfound++; 73 for (int k = 0; k < 4; k++) { 74 int nx = x + mov[k][0], ny = y + mov[k][1], nxid = id(nx, ny); 75 if (!nxid || found[nx][ny]) 76 uf.unit(nxid, id(x, y)); 77 } 78 } 79 res += cfound - uf.siz[uf.find(0)]; 80 } 81 printf("%d", res); 82 } 83 84 int main() { 85 init(); 86 solve(); 87 return 0; 88 }