Promising String (树状数组)

该博客探讨了一种字符串处理问题,要求通过特定操作使字符串中'+++'和'---'的数量相等。提出了利用前缀和和树状数组动态统计的方法,实现了在O(n log n)的时间复杂度内找到所有可转换子串的解决方案。文章详细解释了思路,包括如何通过余数判断子串是否可行,并给出了离散化和偏移量两种实现方式的代码示例。
摘要由CSDN通过智能技术生成

Promising String

[Link](Problem - F2 - Codeforces)

题意

​ 给你一个字符串,当且仅当 + + +的数量等于 − - 的数量时称为好串,给你一个操作可以将任意两个相邻的 − - 变成一个 + + +,问你有多少个子串可以通过操作变成一个好串。

思路

​ 对于任意一个子串若成立里需满足 c n t − − 2 × k = c n t + + k → c n t − − c n t + = 3 × k → c n t − − c n t + ≡ 0 ( m o d   3 ) cnt_- -2\times k=cnt_++k\to cnt_--cnt_+=3\times k \to cnt_--cnt_+ \equiv 0(mod\ 3) cnt2×k=cnt++kcntcnt+=3×kcntcnt+0(mod 3),设 s s s为原串的前缀和(原串 + + + − 1 -1 1减为 1 1 1),则需要满足 s r − s l − 1 ≡ 0 ( m o d   3 ) s_r-s_{l-1}\equiv 0(mod\ 3) srsl10(mod 3),也就是对于任意一个位置 i i i找他前面有多少个位置满足上面的式子并且 s r ≥ s l − 1 s_r\ge s_{l-1} srsl1

​ 对于找某个数前面有多少个满足情况的位置,可以用树状数组动态统计,即开三个分别统计余数 0 , 1 , 2 0,1,2 0,1,2的情况,维护值域即可。

​ 由于有负数,我们可以先离散化一下,然后再维护即可,或者直接同一加一个偏移量,保证所有的数都是大于 0 0 0的即可。

Code

  • 离散化
#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 2e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int op;
int tr[N][3];
int lowbit(int x) {
    return x & -x;
}
void add(int x) {
    for (; x <= m; x += lowbit(x)) tr[x][op] ++;
}
int sum(int x) {
    int res = 0;
    for (; x; x -= lowbit(x)) res += tr[x][op];
    return res;
}
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin >> T;
    while (T -- ) {
        cin >> n;
        string str; cin >> str;
        vector<int> s(n + 1);
        vector<int> ve;        
        for (int i = 1; i <= n; i ++) s[i] = s[i - 1] + (str[i - 1] == '-' ? 1 : -1), ve.push_back(s[i]);
        ve.push_back(0);
        sort(ve.begin(), ve.end());
        ve.erase(unique(ve.begin(), ve.end()), ve.end());
        auto g = [&](int x) {
            return lower_bound(ve.begin(), ve.end(), x) - ve.begin() + 1;
        };
      
        m = ve.size();
        for (int i = 1; i <= m; i ++)
            for (int j = 0; j < 3; j ++)
                tr[i][j] = 0;
    
        LL res = 0;
        op = 0;
        add(g(0));
     
        for (int i = 1; i <= n; i ++) {
            op = (s[i] % 3 + 3) % 3;                   
            res += sum(g(s[i]));          
            add(g(s[i]));
        }

        cout << res << '\n';
    }
    return 0;
}
  • 偏移量
#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 4e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int op;
int tr[N][3];
int lowbit(int x) {
    return x & -x;
}
void add(int x) {
    for (; x <= n * 2 + 1; x += lowbit(x)) tr[x][op] ++;
}
int sum(int x) {
    int res = 0;
    for (; x; x -= lowbit(x)) res += tr[x][op];
    return res;
}
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin >> T;
    while (T -- ) {
        string str;
        cin >> n >> str;
        vector<int> s(n + 1);
        for (int i = 1; i <= n; i ++)
            s[i] = s[i - 1] + (str[i - 1] == '-' ? 1 : -1);
        
        for (int i = 1; i <= n * 2 + 1; i ++)
            for (int j = 0; j < 3; j ++)
                tr[i][j] = 0;
        
        LL res = 0;
        op = 0;
        add(n + 1);
        for (int i = 1; i <= n; i ++) {
            op = (s[i] % 3 + 3) % 3;
            res += sum(s[i] + n + 1);            
            add(s[i] + n + 1);
        }
        
        cout << res << '\n';
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值