Educational Codeforces Round 102 (Rated for Div. 2) D. Program

这篇博客介绍了如何利用线段树解决一个字符串操作问题。给定一个由'+'和'-'组成的字符串,表示前进和后退操作,以及一系列询问区间[l, r],任务是计算在排除[l, r]区间外,能到达的不同位置数量。通过计算最大前缀和和最小前缀和,可以确定最远和最近可达位置,从而得出答案。博主提供了详细的解题思路和C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

链接

题意:

你现在的位置是0号位置,现在给了你一个字符串操作,字符串由±组成,+表示前进,-表示后退,现在有m次询问,每次给你一个区间l、r,问你,出去[l, r]区间以外,其他的操作对你进行,你能去过不同位置的数量。比如:你去过0,-1,-2,-1,那么你去过的位置的数量是3。

思路:

最开始想到这是个数据结构,想到用线段树,但是不知道维护什么。后面想了一下,其实就是让你算:能够走到的最远位置和能够走到的最近位置,求他们之间的差值即可,它们之间的位置肯定是都能够到达的。那么,能够走到的最远位置就是:最大前缀和,能够走到的最近位置就是:最小前缀和。现在是出去[l, r],那么现在就是将区间分为三段了,不要中间那一段,那么就是max(第一段区间和第三段区间的最大前缀和)-min(第一段的和第三段的最小前缀和) + 1,那么它们的差值就是我们要找的值(+1是因为最开始在0号位置,还要将0号位置算上)。(想到这里才想到用线段树来作,因为线段树有一个区间查询的功能。)
然后我们单独用一个前缀和数组来将字符串数组转换过来。第一段区间好算,第三段区间有一个不同的,因为第三段区间是接着第二段区间的,我们只用了一个前缀和数组,所以我们在求第三段的最大前缀和以及最小前缀和的时候要将[l, r]区间内的偏移量减去。

#include <bits/stdc++.h>

#define int long long
#define ul u << 1
#define ur u << 1 | 1

using namespace std;

const int N = 2e5 + 10;

int n, m;
char s[N];
int a[N];

struct node
{
    int l, r;
    int minn, maxx;
}tr[N << 2];

void pushup(int u)
{
    tr[u].maxx = max(tr[ul].maxx, tr[ur].maxx);
    tr[u].minn = min(tr[ul].minn, tr[ur].minn);
}

void build(int u, int l, int r)
{
    tr[u] = {l, r};
    if (l == r) { tr[u].maxx = tr[u].minn = a[l]; return ; }

    int mid = l + r >> 1;
    build(ul, l, mid), build(ur, mid + 1, r);
    pushup(u);
}

int query_min(int u, int l, int r)
{
    if (l <= tr[u].l && r >= tr[u].r) return tr[u].minn;

    int mid = tr[u].l + tr[u].r >> 1;
    int minn = 1e9;
    if (l <= mid) minn = min(minn, query_min(ul, l, r));
    if (r > mid) minn = min(minn, query_min(ur, l, r));
    return minn;
}

int query_max(int u, int l, int r)
{
    if (l <= tr[u].l && r >= tr[u].r) return tr[u].maxx;

    int mid = tr[u].l + tr[u].r >> 1;
    int maxx = -1e9;
    if (l <= mid) maxx = max(maxx, query_max(ul, l, r));
    if (r > mid) maxx = max(maxx, query_max(ur, l, r));
    return maxx;
}

void solve()
{
    cin >> n >> m;
    cin >> (s + 2);

    a[1] = 0;
    for (int i = 2; i <= n + 1; i ++)
    {
        if (s[i] == '+') a[i] = a[i - 1] + 1;
        else a[i] = a[i - 1] - 1;
    }

    build(1, 1, n + 1);

    int l, r;
    while (m --)
    {
        cin >> l >> r;
        l ++, r ++;

        int lmaxx = 0, lminn = 0, rmaxx = 0, rminn = 0;
        lmaxx = query_max(1, 1, l - 1);
        lminn = query_min(1, 1, l - 1);
        rmaxx = query_max(1, min(r + 1, n + 1), n + 1);
        rminn = query_min(1, min(r + 1, n + 1), n + 1);

        int tmp = a[r] - a[l - 1];
        cout << (max(lmaxx, rmaxx - tmp) - min(lminn, rminn - tmp) + 1) << endl;
    }
}

signed main()
{
    std::ios::sync_with_stdio(false);

    int t; cin >> t;
    while (t --) solve();

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值