[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;
}