21. CF-Narrow Components

题目链接:Narrow Components

区间内连通块数量相当于总的块数减去有效的边数。先用前缀和预处理出块数,然后用并查集维护前缀有效边数和。由于是从整个序列左端点开始计算的,查询的时候可能会把左边的一些不连通的块归到一起。然后注意到只有最左边是一段连续的 101 时才可能出现这种情况,所以找到第一个不是 101 的列,分类讨论:

  • 如果所有的列都是 101,答案就是 2 2 2
  • 如果上中下都是 1,说明左边这些都能连到右边去,答案不变
  • 如果上下都是 0,说明这两段都是独立的,答案 + 2 +2 +2
  • 其余情况都是有一个可以和右边连着,另一个独立,答案 + 1 +1 +1

做法来源于官方题解,下面的代码也是照着答案写的。细节稍微有点多。

这题还有一个加强版 P3300 [SDOI2013]城市规划,非常的麻烦。

#include 
using namespace std;
using ll = long long;
#define endl '\n'
const int maxn = 5e5 + 5;
namespace dsu {
int fa[maxn << 2], sz[maxn << 2];
void init(int n) {
    iota(fa, fa + n + 1, 0);
    fill(sz, sz + n + 1, 1);
}
int find(int x) { 
	return fa[x] == x ? fa[x] : (fa[x] = find(fa[x])); 
}
bool merge(int x, int y) {
    x = find(x), y = find(y);
    if (x == y)
        return false;
    if (sz[x] < sz[y])
        swap(x, y);
    fa[y] = x, sz[x] += sz[y];
    return true;
}
}; // namespace dsu
char str[maxn];
int a[3][maxn], tot[maxn];
int ph[maxn], pv[maxn], nxt[maxn];
void solve() {
    int n, q;
    cin >> n;
    for (int i = 0; i < 3; ++i) {
        cin >> (str + 1);
        for (int j = 1; j <= n; ++j)
            a[i][j] = (str[j] == '1');
    }
    dsu::init(n * 3);
    for (int i = 1; i <= n + 1; ++i) {
    	tot[i + 1] += tot[i];
        for (int j = 0; j < 3; ++j)
            tot[i + 1] += a[j][i];
    }
    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j < 2; ++j) {
            if (a[j][i] && a[j + 1][i] && dsu::merge(j * n + i, j * n + n + i))
                pv[i + 1]++;
        }
        for (int j = 0; j < 3; ++j) {
            if (i > 1 && a[j][i] && a[j][i - 1] &&
                dsu::merge(j * n + i, j * n - 1 + i))
                ph[i]++;
        }
    }
    for (int i = 1; i <= n + 1; ++i) {
        pv[i] += pv[i - 1], ph[i] += ph[i - 1];
    }
    for (int i = n; i >= 1; --i) {
        if (a[0][i] && !a[1][i] && a[2][i])
            nxt[i] = nxt[i + 1] + 1;
    }
    cin >> q;
    while (q--) {
        int l, r;
        cin >> l >> r;
        int pos = l + nxt[l];
        if (pos > r) {
            cout << 2 << endl;
            continue;
        }
        int cnt = tot[r + 1] - tot[pos];
        int edg = pv[r + 1] - pv[pos] + ph[r] - ph[pos];
        int ans = cnt - edg;
        if (pos != l) {
            if (a[0][pos] && a[1][pos] && a[2][pos])
                ;
            else if (!a[0][pos] && !a[2][pos])
                ans += 2;
            else
                ans++;
        }
        cout << ans << endl;
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T = 1;
    // cin >> T;
    while (T--) {
        solve();
    }
}

你可能感兴趣的:(一题,算法,c++)