AT_abc441_e 学习笔记
题内话
省流:这题脑洞大开,场切的。
题意
给定一个由 \(A, B, C\) 三种字符组成的长度为 \(n\) 的字符串 \(s\),求 \(s\) 的 \(\frac{n(n+1)}{2}\) 个子串中 \(A\) 的个数严格大于 \(B\) 的个数的子串数量。
若两个子串相同,但是它们分别在 \(s\) 的不同位置,算作两个子串。
首先这个题目如果暴力枚举,时间复杂度将会高达 \(O(n^2)\),在 \(n \le 5 \times 10^5\) 时显然会被 T 飞,我们需要优化。
需要脑洞的来了,我们令:
\(A: -1\)
\(B: 1\)
\(C: 0\)
然后对这个 \(s\) 求前缀和,问题就转化为前缀和数组 \(pre\) 中逆序对的个数,然后你就可以把尘封已久的这个题目的代码再写一遍,就行了。
这里的实现方式多样,看题解区都是树状数组,这里给一个归并排序(分治)的实现。
code
#include <bits/stdc++.h>
#define Ofile(s) freopen(s".in", "r", stdin), freopen (s".out", "w", stdout)
#define Cfile(s) fclose(stdin), fclose(stdout)
#define fast ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
using namespace std;
using ll = long long;
using ull = unsigned long long;
using lb = long double;
using pii = pair<ll, ll>;
using pll = pair<ll, ll>;
using pil = pair<ll, ll>;
using pli = pair<ll, ll>; // 缺省源请忽略
constexpr ll mod = 998244353;
constexpr ll maxk = 5e5 + 5;
ll n;
ll ans;
ll pre[maxk], b[maxk];
string s;
map <char, ll> sre; // 懒得打,用 map 初始化一下
void mergesort(ll l, ll r) {
if (l == r)
return;
ll mid = (l + r) >> 1;
mergesort(l, mid);
mergesort(mid + 1, r);
ll p1 = l, p2 = mid + 1, p3 = l;
while (p1 <= mid && p2 <= r)
if (pre[p1] <= pre[p2])
b[p3] = pre[p1], ++p3, ++p1;
else
b[p3] = pre[p2], ++p3, ++p2, ans += mid - p1 + 1;
while (p1 <= mid)
b[p3] = pre[p1], ++p3, ++p1;
while (p2 <= r)
b[p3] = pre[p2], ++p3, ++p2;
for (ll i = l; i <= r; i++)
pre[i] = b[i];
} // 逆序对板子
int main() {
freopen("std.in", "r", stdin);
freopen("std.out", "w", stdout);
fast;
cin >> n;
cin >> s;
sre['A'] = -1;
sre['B'] = 1;
sre['C'] = 0; // 小脑洞
for (ll i = 0; i < (ll)s.size(); i++)
pre[i + 1] = pre[i] + sre[s[i]]; // 前缀和
mergesort(0, n); // 注意这里是 0~n,不要写成 1~n 或者是 0~n-1
cout << ans;
return 0;
}

浙公网安备 33010602011771号