「JOI Open 2019」病毒实验(bfs+Boruvka算法思想):
题解:
考虑先预处理一个数组mx[S],其中S是一个二进制状态,记录着四个方向是否有病毒,在这种情况下,在那个字符串环上的最长连续段(注意这是个无限长的环,最长连续段可以是+∞)。
那么得到一种暴力的做法,枚举起点,然后宽搜,对于每一个点,在预处理之后,是可以\(O(1)\)判断是否会被感染的。
因为我们要求的是最小的,考虑如果以u作为起点,能走到v,那么v的答案比起u只会小不会大。
考虑把矩阵划分成若干块,一开始每个点自己为一块。
每一块里有一个至少特殊点x(我们只记录一个就好了),以这个块的所有点作为起点,都能感染到这个特殊点。
每次枚举一个块A,从A的特殊点开始宽搜,如果走到另一个块B的点,那么就把A和B合并到一起,新的特殊点是B原来的特殊点。
如果一个块,不能走到其它块去,便用它更新答案,特殊点能遍历到的点数便是这个块的最小答案,同时是方案数。
这么做复杂度还是没有保证,会被卡到\(O((nm)^2)\)。
考虑Boruvka算法求最小生成树是怎么做的:
每次给每一个联通块找一条出边,每次做完,必定能使联通块个数/2,所以只用做log次。
对这题,是一样的,每次先给每一个块A找一个要合并的B:
注意!这一层里,如果先通过A找到了相邻的B,把A合并到B后,那么这一层里不能再用B的特殊点BFS,原因是这样可能重复经过A的点,这就是会被卡的原因,但是,如又有其它的C找到B,把C合并到B,是没有问题的。
复杂度:\(O(nm~log~nm)\)
Code:
#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i < _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;
int t, n, m;
int mov[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
int zh(char c) {
if(c == 'N') return 0;
if(c == 'E') return 1;
if(c == 'S') return 2;
return 3;
}
const int M = 2e5 + 5;
char str[M]; int c[M];
const int N = 805;
int a[N][N];
void Init() {
scanf("%d %d %d", &t, &n, &m);
scanf("%s", str + 1);
fo(i, 1, t) c[i] = c[i + t] = zh(str[i]);
fo(i, 1, n) fo(j, 1, m) {
scanf("%d", &a[i][j]);
}
}
int mx[16];
void build() {
fo(s, 0, 15) {
int l = 0;
fo(j, 1, 2 * t + 1) if(j > 2 * t || !(s >> c[j] & 1)) {
mx[s] = max(mx[s], l);
l = 0;
} else l ++;
if(mx[s] == 2 * t) mx[s] = 1e5;
}
}
int bz[N][N], bz0;
int pd(int x, int y) {
if(a[x][y] == 0) return 0;
int s = 0;
fo(j, 0, 3) {
int u = x + mov[j][0], v = y + mov[j][1];
s += (1 << j) * (bz[u][v] == bz0);
}
return a[x][y] <= mx[s];
}
#define pb push_back
int id[N][N], f[N * N];
int d[N * N][2], d0;
int ok[N][N], us[N * N];
vector<int> g[N * N];
void work() {
fo(i, 1, n) fo(j, 1, m) {
id[i][j] = (i - 1) * m + j;
f[id[i][j]] = id[i][j];
g[id[i][j]].pb(id[i][j]);
if(a[i][j] == 0) ok[i][j] = 1;
}
int ans = 1e9, ans2 = 0;
while(1) {
int br = 1;
fo(i, 1, n * m) us[i] = 0;
fo(sx, 1, n) fo(sy, 1, m) if(!us[id[sx][sy]] && !ok[sx][sy]) {
br = 0;
bz0 ++;
int w = id[sx][sy];
d[d0 = 1][0] = sx; d[1][1] = sy;
bz[sx][sy] = bz0;
int fin = 1;
for(int i = 1; i <= d0; i ++) {
int x = d[i][0], y = d[i][1];
fo(j, 0, 3) {
int u = x + mov[j][0], v = y + mov[j][1];
if(u && v && u <= n && v <= m && bz[u][v] != bz0 && pd(u, v)) {
if(f[id[u][v]] == f[id[x][y]]) {
d[++ d0][0] = u; d[d0][1] = v;
bz[u][v] = bz0;
} else {
int q = f[id[u][v]];
ff(k, 0, g[w].size()) {
f[g[w][k]] = q;
g[q].pb(g[w][k]);
}
g[w].clear();
us[q] = 1; ok[sx][sy] = 1;
fin = 0; break;
}
}
}
if(!fin) break;
}
if(fin) {
if(d0 < ans)
ans = ans2 = d0; else
if(d0 == ans)
ans2 += d0;
ok[sx][sy] = 1;
}
}
if(br) break;
}
pp("%d\n%d\n", ans, ans2);
}
int main() {
Init();
build();
work();
}
转载注意标注出处:
转自Cold_Chair的博客+原博客地址