952 字
5 分钟
abc455
E - Unbalanced ABC Substrings
思路
暴力做法
首先考虑暴力做法,我们枚举每一个子串可能的左端点并扩展右端点,在过程中动态维护的出现次数,时间复杂度
评测结果:https://atcoder.jp/contests/abc455/submissions/75267993
AC 15,TLE 21
Code
// Problem: AT ABC455 E// Contest: AtCoder - Sky Inc, Programming Contest 2026 (AtCoder Beginner Contest 455)// URL: https://atcoder.jp/contests/abc455/tasks/abc455_e// Time: 2026-04-25 20:00:07
#include <bits/stdc++.h>using namespace std;
#define endl '\n'
typedef long long ll;typedef unsigned long long ull;typedef pair<int, int> pii;typedef pair<double, double> pdd;
const int dx[] = {-1, 0, 1, 0, -1, 1, 1, -1};const int dy[] = {0, 1, 0, -1, 1, 1, -1, -1};const int inf = 0x3f3f3f3f;const int N = 0;
int n;string s;ll res;
bool check(int &a, int &b, int &c){ return (a != b && b != c && a != c);}
int main(){ cin >> n >> s;
for (int i = 0; i < n; i++) { int a = 0, b = 0, c = 0; for (int j = i; j < n; j++) { if (s[j] == 'A') a++; if (s[j] == 'B') b++; if (s[j] == 'C') c++;
if (check(a, b, c)) res++; } }
cout << res << endl; return 0;}正解
我们发现正着算“出现次数互不相同的子串”比较困难,我们考虑统计这个集合的补集,也就是有多少个子串中,至少有两个字符的出现次数相同。
我们假设:
- 集合 :子串中
A的数量 =B的数量 - 集合 :子串中
B的数量 =C的数量 - 集合 :子串中
C的数量 =A的数量
则我们要求的答案为
根据容斥原理,我们可以展开上述公式如下:
这里有一个关键的逻辑:如果 A = B 且 B = C,那么自然有A = B = C,假设全相等的子串数有个,化简上述公式,可以得到
如何快速求出 (即 A 和 B 数量相等的子串)呢?我们可以使用前缀和的差值。
遍历字符串 ,记录到当前位置为止 A、B、C 的出现次数 。
对于任意一个子串 ,如果它里面 A 和 B 的数量相等,意味着:
移项可得:
结论:只要两个位置的前缀和差值 相等,这两个位置之间的子串中 A 和 B 的数量就相等。
同理:
- 记录 的频率来求
- 记录 的频率来求
- 记录 的频率来求
Code
// Problem: AT ABC455 E// Contest: AtCoder - Sky Inc, Programming Contest 2026 (AtCoder Beginner Contest 455)// URL: https://atcoder.jp/contests/abc455/tasks/abc455_e// Time: 2026-04-25 20:00:07#include <bits/stdc++.h>using namespace std;
#define endl '\n'
typedef long long ll;typedef unsigned long long ull;typedef pair<int, int> pii;typedef pair<double, double> pdd;typedef pair<long long, long long> pll;
const int dx[] = {-1, 0, 1, 0, -1, 1, 1, -1};const int dy[] = {0, 1, 0, -1, 1, 1, -1, -1};const int inf = 0x3f3f3f3f;const int N = 0;
int n;string s;ll res;
int main(){ cin >> n >> s;
ll tot = 1LL * n * (n + 1) / 2;
map<ll, ll> fab, fbc, fca; map<pll, ll> fabc;
ll a = 0, b = 0, c = 0;
fab[0] = 1; fbc[0] = 1; fca[0] = 1; fabc[{0, 0}] = 1;
for (auto ch: s) { if (ch == 'A') a++; else if (ch == 'B') b++; else if (ch == 'C') c++;
ll dab = a - b; ll dbc = b - c; ll dca = c - a;
fab[dab]++; fbc[dbc]++; fca[dca]++; fabc[{dab, dbc}]++; }
ll nab = 0, nbc = 0, nca = 0, nabc = 0;
for (auto const &[k, v]: fab) nab += v * (v - 1) / 2; for (auto const &[k, v]: fbc) nbc += v * (v - 1) / 2; for (auto const &[k, v]: fca) nca += v * (v - 1) / 2; for (auto const &[k, v]: fabc) nabc += v * (v - 1) / 2;
res = tot - nab - nbc - nca + 2 * nabc;
cout << res << endl; return 0;}