「JOI Open 2019」病毒实验(bfs+Boruvka算法思想):

https://loj.ac/problem/3155

题解:

考虑先预处理一个数组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();
}
posted @ 2020-03-02 23:10  Cold_Chair  阅读(550)  评论(0编辑  收藏  举报