codeforces1473D Program(线段树 or 线形dp优化)

传送门
题解:思考一下可以知道在一个区间内连续进行 + / − +/- +/操作后,这个区间的 x x x的变化是连续的,也就是找到变化中的最大值和最小值即可,最后也就是维护下区间最大最小值而已,对于中间跳过一段操作,对于后面的影响无非就是减去这段对于 x x x的影响罢了。

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int n, m, x, y, a[N];
string s;
struct node{
    
    
    int l, r, mx, mn;
};
node tree[N << 2];
void build(int p, int l, int r)
{
    
    
    tree[p].l = l; tree[p].r = r;
    if (l == r) {
    
    
        tree[p].mx = tree[p].mn = a[l];
        return ;
    }
    int mid = (l + r) >> 1;
    build(p << 1, l, mid);
    build(p << 1 | 1, mid + 1, r);
    tree[p].mx = max(tree[p << 1].mx, tree[p << 1 | 1].mx);
    tree[p].mn = min(tree[p << 1].mn, tree[p << 1 | 1].mn);
}
pair<int, int> ask(int p, int l, int r)
{
    
    
    if (tree[p].l >= l && tree[p].r <= r) {
    
    
        return {
    
    tree[p].mx, tree[p].mn};
    }
    int mid = (tree[p].l + tree[p].r) >> 1;
    if (r <= mid) {
    
    
        return ask(p << 1, l, r);
    } else if (l > mid) {
    
    
        return ask(p << 1 | 1, l, r);
    } else {
    
    
        auto tl = ask(p << 1, l, mid), tr = ask(p << 1 | 1, mid + 1, r);
        return {
    
    max(tl.first, tr.first), min(tl.second, tr.second)};
    }
}
int main()
{
    
    
    int T;
    cin >> T;
    while (T--) {
    
    
        cin >> n >> m >> s;
        for (int i = 0; i < n; i++) {
    
    
            a[i+2] = a[i+1] + (s[i] == '+' ? 1 : -1);
        }
        n += 2; a[n] = a[n - 1];
        build(1, 1, n);
        for (int i = 0; i < m; i++) {
    
    
            cin >> x >> y;
            x += 1; y += 1;
            int t = a[y] - a[x - 1];
            auto l = ask(1, 1, x - 1), r = ask(1, y, n);
            r.first -= t; r.second -= t;
            cout << max(l.first, r.first) - min(l.second, r.second) + 1 << endl;
        }
    }
    return 0;
}

如上我的做法为 O ( n l o g n ) O(nlogn) O(nlogn)级别,官方题解为 O ( n ) O(n) O(n)级别
思想还是那样的思想,但是维护就得思考一番,考虑用 p r [ i ] pr[i] pr[i]表示当前到了第 i i i位时的数字是多少, p r l [ i ] 、 p r r [ i ] prl[i]、prr[i] prl[i]prr[i]为前 i i i长度的最小的,最大的,维护非常简单
p r l [ i ] = m i n ( p r l [ i − 1 ] , p r [ i ] + d ) p r r [ i ] = m a x ( p r r [ i − 1 ] , p r [ i ] + d ) prl[i]=min(prl[i-1],pr[i]+d)\\ prr[i]=max(prr[i-1],pr[i]+d) prl[i]=min(prl[i1],pr[i]+d)prr[i]=max(prr[i1],pr[i]+d)
但是后缀就有点技巧了,考虑这样一件事情,如果已经知道 s u l [ i ] sul[i] sul[i]表示从第 i i i位开始的最小的,那么第 i − 1 i-1 i1位呢,肯定是 s u l [ i − 1 ] = s u l [ i ] + d sul[i-1]=sul[i]+d sul[i1]=sul[i]+d,如果为正数也就是 1 1 1,那么改为 0 0 0即可,如果不是这个式子,那换成其他的只能更大,不可能最小,同理最大也是一样的。
最后维护答案时直接维护 p r [ l − 1 ] pr[l-1] pr[l1]的值即可

#include <bits/stdc++.h>
using namespace std;
int n, m, l, r;
string s;
int main()
{
    
    
    int T;
    cin >> T;
    while (T--) {
    
    
        cin >> n >> m >>s;
        vector<int> sul(1, 0), sur(1, 0), prl(1, 0), prr(1, 0), pr(1, 0);
        for (int i = n - 1; i >= 0; i--) {
    
    
            int d = s[i] == '+' ? 1 : -1;
            sul.emplace_back(min(0, sul.back() + d));
            sur.emplace_back(max(0, sur.back() + d));
        }
        reverse(sul.begin(), sul.end());
        reverse(sur.begin(), sur.end());
        for (int i = 0; i < n; i++) {
    
    
            int d = s[i] == '+' ? 1 : -1;
            pr.emplace_back(pr.back() + d);
            prl.emplace_back(min(prl.back(), pr.back()));
            prr.emplace_back(max(prr.back(), pr.back()));
        }
        for (int i = 0; i < m; i++) {
    
    
            cin >> l >> r;
            l--;
            int l1 = prl[l], r1 = prr[l];
            int l2 = sul[r] + pr[l], r2 = sur[r] + pr[l];
            cout << max(r1, r2) - min(l1, l2) + 1 << endl;
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zhouzi2018/article/details/112743371
今日推荐