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

/**********************************************************
 * Author        : dingziyang888
 * Website       : https://www.luogu.com.cn/problem/AT_abc441_e
 * Created Time  : (不记得了)
 * FileName      : AT_abc441_e.cpp
 * Warning!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 * 1.MLE?
 * 2.array size enough?
 * 3.long long?
 * 4.overflow long long?
 * 5.multiple task cleaned?
 * 6.freopen?
 * 7.TLE?
 * *******************************************************/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <iterator>
#include <map>
#include <unordered_map>
#include <queue>
#include <string>
#include <cstring>
#include <set>
#include <bitset>
#include <unordered_set>
#include <vector>
#include <deque>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <list>
#include <array>
#include <iterator>
#include <cmath>
#include <new>
#include <random>
#include <cfloat>
#include <cstdlib>
#include <climits>
#include <numeric>
#include <complex>
#include <ctime>
#include <chrono>
#include <thread>
#include <mutex>
#include <future>
#include <exception>
#include <stdexcept>
#include <cstdint>
#include <cassert>
#include <stack>
#include <cctype>
#define DEBUG
#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<int, int>;
using pll = pair<ll, ll>;
using pil = pair<int, ll>;
using pli = pair<ll, int>; // 缺省源请忽略

constexpr int mod = 998244353;
constexpr int maxk = 500005;

int n;
ll ans;

int pre[maxk], b[maxk];

string s;

map <char, int> sre; // 懒得打,用 map 初始化一下

void mergesort(int l, int r) {
    if (l == r)
        return;
    int mid = (l + r) >> 1;
    mergesort(l, mid);
    mergesort(mid + 1, r);
    int 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 (int i = l; i <= r; i++)
        pre[i] = b[i];
} // 逆序对板子

int main() {
    fast;
    cin >> n;
    cin >> s;
    sre['A'] = -1;
    sre['B'] = 1;
    sre['C'] = 0; // 小脑洞
    for (int i = 0; i < (int)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;
}
posted @ 2026-02-02 19:15  constexpr_ll  阅读(0)  评论(0)    收藏  举报