「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();
}

猜你喜欢

转载自www.cnblogs.com/coldchair/p/12398836.html
今日推荐