Codeforces 2131F Unjust Binary Life 题解 [ 绿 ] [ 前缀和 ]
Unjust Binary Life:简单题,感觉一大把 *1900 比这个难啊。
首先注意到靠这种方式生成的网格图有比较良好的性质,其他题有一些什么行与列相互独立的结论,但这题直接把 \((1,1)\) 到 \((x, y)\) 的路线画一下,你就能发现只有把第 \(1\sim x\) 行和第 \(1\sim y\) 列全部搞成相同的数才能走到 \((x, y)\)。证明直接随便画一条路线,然后对于路径上的每一个格子,将对应的行和对应的列连边,表示它们所代表的值必须相等(因为只有相等了异或和才是 \(0\))。最后会发现第 \(1\sim x\) 行、第 \(1\sim y\) 列全部在一个连通块内,也就说明了这些数必须全部相等。
令 \(A\) 为 \(a\) 数组的前缀和,\(B\) 为 \(b\) 数组的前缀和。那么问题被转化为求 \(\sum_{ 1\le i, j \le n}\min\{X_i+Y_j,i+j-X_i-Y_i \}\)。
发现 \(X_i+Y_j \le i+j-X_i-Y_i\) 成立当且仅当 \(2X_i - i \le j - 2Y_j\) 成立,所以把每个 \(2X_i - i\) 丢进桶里。最后对桶做一遍前缀和,枚举 \(j\) 的值统计对应的贡献即可。注意 \(2X_i - i,j - 2Y_j\) 可能是负数所以需要偏移下标。时间复杂度 \(O(n)\)。
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
const int N = 200005;
int n, a[N], b[N];
ll ans, totx[2 * N], toty[2 * N], totz[2 * N], sma;
void solve()
{
cin >> n;
sma = ans = 0;
for(int i = 0; i <= 2 * n; i++) totx[i] = toty[i] = totz[i] = 0;
for(int i = 1; i <= n; i++)
{
char c;
cin >> c;
a[i] = c - '0';
a[i] += a[i - 1];
totx[2 * a[i] - i + n] ++;
toty[2 * a[i] - i + n] += a[i];
totz[2 * a[i] - i + n] += i - a[i];
sma += i - a[i];
}
for(int i = 1; i <= n; i++)
{
char c;
cin >> c;
b[i] = c - '0';
b[i] += b[i - 1];
}
for(int i = 1; i <= 2 * n; i++)
{
totx[i] += totx[i - 1];
toty[i] += toty[i - 1];
totz[i] += totz[i - 1];
}
for(int i = 1; i <= n; i++)
{
ans += toty[i - 2 * b[i] + n] + totx[i - 2 * b[i] + n] * b[i];
ans += (sma - totz[i - 2 * b[i] + n]) + (n - totx[i - 2 * b[i] + n]) * (i - b[i]);
}
cout << ans << "\n";
}
int main()
{
//freopen("sample.in", "r", stdin);
//freopen("sample.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--) solve();
return 0;
}

浙公网安备 33010602011771号