洛谷P1173 [NOI2016]网格
这个码量绝对是业界大毒瘤......
300行,6.5k,烦的要死......
题意:给你一个网格图,里面有0或1。你需要把一些0换成1使得存在某两个0不四联通。输出最小的换的数量。无解-1。
n,m<=1e9,网格中1的数量<=1e5,多组数据。
首先我们发现,最多只要2就行了(围住一个角落),所以答案是[-1,2]中的整数。
然后考虑何时为-1:0的数目小于2或等于2且相连。
何时为0:图初始就不连通。
何时为1:图中存在割点。
除此之外就是2了。
然后发现图很大,c很小,考虑离散化。
然后发现我们只要把每个1周围的点提取出来即可。
提取3×3是错误的,有个众人皆知的样例:
0 0 0
0 0 0
0 0 1
显然提取之后会有一个割点在原图正中间,但是实际上它并不是割点。
然后我们暴力一点,提取5×5即可......
算法流程:提取点,编号。然后判断联通性。然后做tarjan,判断割点。
然后又有好多坑点...比如割点必须在某个1的周围3×3区域(易证),如果忽视这个就会出现一种毒瘤情况:
1 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 1
可以发现在奇怪的地方出现了割点...
然后还要特判,(n - 1)(m - 1) = 0的时候答案不可能为2。
然后怒写一天半终于对了,又发现map太慢跑不过......手写hash。
终于A了....然后uoj日常97分......
[update]如何判断答案为0:对那些提取出来的非关键点进行并查集。然后枚举每个关键点连通块,如果某个关键点连通块连着两个并查集,答案为0。
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 5 inline void read(int &x) { 6 x = 0; 7 char c = getchar(); 8 while(c < '0' || c > '9') { 9 c = getchar(); 10 } 11 while(c >= '0' && c <= '9') { 12 x = (x << 3) + (x << 1) + c - 48; 13 c = getchar(); 14 } 15 return; 16 } 17 18 const int N = 100010; 19 const int dx[4] = {0, 1, 0, -1}; 20 const int dy[4] = {1, 0, -1, 0}; 21 22 const int MO = 19260817, B = 998244353; 23 struct POS { 24 int x, y, h; 25 POS(int xx = 0, int yy = 0) { 26 x = xx; 27 y = yy; 28 h = (1ll * x * B + y) % MO; 29 if(h < 0) { 30 h += MO; 31 } 32 } 33 inline bool operator ==(const POS &d) const { 34 return x == d.x && y == d.y; 35 } 36 }; 37 struct Node { 38 int nex, val; 39 POS p; 40 }node[N * 30]; int top; 41 struct MAP { 42 int e[MO]; 43 inline void insert(const POS &d, const int &a) { 44 node[++top].val = a; 45 node[top].nex = e[d.h]; 46 node[top].p = d; 47 e[d.h] = top; 48 return; 49 } 50 inline int find(const POS &d) { // if not exist return 0 51 for(int i = e[d.h]; i; i = node[i].nex) { 52 if(node[i].p == d) { 53 return node[i].val; 54 } 55 } 56 return 0; 57 } 58 inline void clear() { 59 memset(e, 0, sizeof(e)); 60 return; 61 } 62 }mp, use; 63 64 int n, m, c, xi[N], yi[N], tot, num, root; 65 int dfn[N * 25], low[N * 25], vis[N * 25]; 66 bool cut[N * 25], vis_c[N], OK; 67 68 inline void np(int x, int y) { 69 if(!mp.find(POS(x, y)) && !use.find(POS(x, y))) { 70 mp.insert(POS(x, y), ++tot); 71 } 72 return; 73 } 74 75 inline int get(int x, int y) { 76 return mp.find(POS(x, y)); 77 } 78 79 void tarjan(int s, int x, int y) { 80 dfn[s] = low[s] = ++num; 81 int temp = 0; 82 for(int i = 0; i < 4; i++) { 83 int t = get(x + dx[i], y + dy[i]); 84 if(!t) { 85 continue; 86 } 87 if(!dfn[t]) { 88 tarjan(t, x + dx[i], y + dy[i]); 89 low[s] = std::min(low[s], low[t]); 90 if(low[t] >= dfn[s]) { 91 temp++; 92 } 93 } 94 else { 95 low[s] = std::min(low[s], dfn[t]); 96 } 97 } 98 if(temp >= 2 || (temp == 1 && s != root)) { 99 cut[s] = 1; 100 } 101 return; 102 } 103 104 void DFS_1(int s, int x, int y, int temp) { 105 vis[s] = temp; 106 for(int i = 0;i < 4; i++) { 107 int t = get(x + dx[i], y + dy[i]); 108 if(!t) { 109 continue; 110 } 111 if(!vis[t]) { 112 DFS_1(t, x + dx[i], y + dy[i], temp); 113 } 114 } 115 return; 116 } 117 118 bool fd; 119 int number; 120 121 bool DFS_2(int s, int x, int y) { 122 vis_c[s] = 1; 123 for(int i = 0; i < 4; i++) { 124 if(use.find(POS(x + dx[i], y + dy[i]))) { 125 int ed = use.find(POS(x + dx[i], y + dy[i])); 126 if(vis_c[ed]) { 127 continue; 128 } 129 int t = DFS_2(ed, x + dx[i], y + dy[i]); 130 if(!t) { 131 return 0; 132 } 133 } 134 else if(get(x + dx[i], y + dy[i])) { 135 if(!fd) { 136 number = vis[get(x + dx[i], y + dy[i])]; 137 fd = 1; 138 } 139 else if(number != vis[get(x + dx[i], y + dy[i])]) { 140 OK = 0; 141 return 0; 142 } 143 } 144 } 145 return 1; 146 } 147 148 inline bool check() { 149 OK = 1; 150 int temp = 0; 151 for(int i = 1; i <= c; i++) { 152 for(int x = xi[i] - 2; x <= xi[i] + 2; x++) { 153 for(int y = yi[i] - 2; y <= yi[i] + 2; y++) { 154 if(vis_c[i]) { 155 continue; 156 } 157 if(mp.find(POS(x, y)) && !vis[get(x, y)]) { 158 ++temp; 159 DFS_1(get(x, y), x, y, temp); 160 goto f1; 161 } 162 } 163 } 164 f1: 165 if(!vis_c[i]) { 166 fd = 0; 167 DFS_2(i, xi[i], yi[i]); 168 } 169 if(!OK) { 170 break; 171 } 172 } 173 return !OK; 174 } 175 176 inline int solve() { 177 read(n); 178 read(m); 179 read(c); 180 if(!c) { 181 if(n == 1 && m == 1) { 182 return -1; 183 } 184 if(n == 1 || m == 1) { 185 if(n == 2 || m == 2) { 186 return -1; 187 } 188 return 1; 189 } 190 return 2; 191 } 192 for(int i = 1; i <= c; i++) { 193 read(xi[i]); 194 read(yi[i]); 195 use.insert(POS(xi[i], yi[i]), i); 196 } 197 if(c + 1 >= 1ll * n * m) { 198 return -1; 199 } 200 for(int i = 1; i <= c; i++) { 201 for(int x = xi[i] - 2; x <= xi[i] + 2; x++) { 202 for(int y = yi[i] - 2; y <= yi[i] + 2; y++) { 203 if(x > 0 && y > 0 && x <= n && y <= m && (x != xi[i] || y != yi[i])) { 204 np(x, y); 205 } 206 } 207 } 208 } 209 if(check()) { 210 return 0; 211 } 212 if(c + 2 == 1ll * n * m) { 213 return -1; 214 } 215 if(m == 1 || n == 1) { 216 return 1; 217 } 218 for(int i = 1; i <= c; i++) { 219 for(int x = xi[i] - 2; x <= xi[i] + 2; x++) { 220 for(int y = yi[i] - 2; y <= yi[i] + 2; y++) { 221 if(!use.find(POS(x, y))) { 222 root = get(x, y); 223 if(dfn[root]) { 224 continue; 225 } 226 tarjan(root, x, y); 227 } 228 } 229 } 230 } 231 232 for(int i = 1; i <= c; i++) { 233 for(int x = xi[i] - 1; x <= xi[i] + 1; x++) { 234 for(int y = yi[i] - 1; y <= yi[i] + 1; y++) { 235 int s = get(x, y); 236 if(cut[s]) { 237 return 1; 238 } 239 } 240 } 241 } 242 return 2; 243 } 244 245 inline void clear() { 246 mp.clear(); 247 use.clear(); 248 memset(dfn + 1, 0, tot * sizeof(int)); 249 memset(low + 1, 0, tot * sizeof(int)); 250 memset(cut + 1, 0, tot * sizeof(bool)); 251 memset(vis + 1, 0, tot * sizeof(int)); 252 memset(vis_c + 1, 0, c * sizeof(bool)); 253 tot = 0; 254 num = 0; 255 top = 0; 256 return; 257 } 258 259 int main() { 260 int T; 261 read(T); 262 while(T--) { 263 printf("%d\n", solve()); 264 if(T) { 265 clear(); 266 } 267 } 268 return 0; 269 }
找个时间在uoj上A一A。