[2021 ICPC 上海 J] Two Binary Strings Problem

https://ac.nowcoder.com/acm/contest/24872/J

题意:
定义 \(f(l, r)\) 为区间内 \(1\) 的数量严格大于一半时出 \(1\),否则出 \(0\)

现给出 \(01\)\(a\), \(b\),问对于所有长度 \(k\),是否能使得,对于 \(a_i\)\(f(max(i−k+1,1),i) = b_i\)

思路:
看数据范围再看 \(01\) 串,一眼 \(bitset\) 递推。求数量关系的经典做法是把 \(0\) 看成 \(-1\),求一下前缀和 \(pre\),若 \(pre_i - pre_j \gt 0\),则代表区间 \(1\) 的数量大于 \(0\)

从这里出发,我们可以想到对于每个 \(i\) 来说,前面所有前缀和比当前小的地方最后都是 \(1\),每次重新找肯定不合算。我们把前缀和函数画出来。

设当前点为 \(i\),上一个前缀和为 \(pre_i\) 的点为 \(j\)。对于所有位置来说,我们肯定要找前面最近的和当前点前缀和相同的点,继承过来。接下来位置分成了两种情况,上升和下降。

对于上升的函数段,由于 \(pre_i \gt pre_{i-1}\),所以前缀和小于 \(i-1\) 的所有点也都小于 \(i\),那么我直接继承过来就行了;对于前缀和正好等于 \(pre_{i-1}\) 的点,由于中间可能是凹凸不平的,我们不能很好的直接找到一个点继承过来,所以我们需要遍历 \([i,j]\) 之间所有 \(pre_u=pre_i-1\) 的点 \(u\),并将对应位置标 \(1\)

对于下降的函数段,我们继承 \(j\) 就行。

最后注意不要忘记了 \(pre_i-pre_0\) 的情况。

我们再遍历一下所有点,将 \(bitset\) 位移到对应的 \(k\),处理一下前面超出的情况,将结果和答案与一下,最后输出即可。

#include <bits/stdc++.h>

using namespace std;

inline int rd() {
    int f = 0; int x = 0; char ch = getchar();
    for (; !isdigit(ch); ch = getchar()) f |= (ch == '-');
    for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
    if (f) x = -x;
    return x;
}

typedef long long ll;

const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
bitset<50050> sm[50050], smk;
bitset<50050> ans;
void solve() {
    int n = rd();
    for (int i = 1; i <= n; ++i) {
        sm[i].reset();
    }
    smk.set();
    ans.set();
    vector<int> pre(n + 1), a(n + 1), b(n + 1);
    vector< vector<int> > pos(2 * n + 1), tmp(2 * n + 1);
    for (int i = 1; i <= n; ++i) {
        scanf("%1d", &a[i]);
        if (a[i] == 0) a[i] = -1;
    }
    for (int i = 1; i <= n; ++i) {
        scanf("%1d", &b[i]);
    }
    for (int i = 1; i <= n; ++i) {
        pre[i] = a[i] + pre[i - 1];
    }
    for (int i = n; i >= 1; --i) {
        pos[pre[i] + n].push_back(i);
        tmp[pre[i] + n].push_back(i);
    }

    for (int i = 1; i <= n; ++i) {
        if (pre[i] > pre[i - 1]) {
            sm[i] |= sm[i - 1];
            while (!tmp[pre[i - 1] + n].empty() && tmp[pre[i - 1] + n].back() <= i) {
                sm[i][tmp[pre[i - 1] + n].back()] = 1; // 将pre{i-1} - prej = 0的位置置为 1
                tmp[pre[i - 1] + n].pop_back();
            }
        }
        sm[i] |= sm[pos[pre[i] + n].back()];
        if (pre[i] > 0) sm[i].set(0); // sm[i][j] 代表 (j+1, i) 中 1 的数量是否大于 0
        if (pos[pre[i] + n].back() != i) pos[pre[i] + n].pop_back();
    }
    for (int i = 1; i <= n; ++i) {
        smk.reset(n - i + 1);
        if (b[i] == 1) {
            sm[i] <<= (n - i + 1);
            if (pre[i] > 0) sm[i] |= smk; // 对 k > i 赋值
            ans &= sm[i];
        } else {
            sm[i].flip();
            sm[i] <<= (n - i + 1);
            if (pre[i] <= 0) sm[i] |= smk;
            ans &= sm[i];
        }
    }
    for (int i = n; i >= 1; --i) {
        cout << ans[i];
    }
    puts("");
}

int main() {
#ifndef stff577
    //ios::sync_with_stdio(false);
    //cin.tie(nullptr);cout.tie(nullptr);
    //cout << fixed << setprecision(20);
#endif
    int t = 1;
    t = rd();
    while (t--) solve();
    return 0;
}
posted @ 2022-06-09 08:28  stff577  阅读(436)  评论(0编辑  收藏  举报