codeforces1473Dプログラム(線分ツリーまたは線形dp最適化)

ポータル
ソリューション:考えてみてください。間隔+ / − + /-で継続的に実行できることがわかります。+ / 操作後、この間隔でxxxの変化は連続的です。つまり、変化の最大値と最小値を見つけるだけで十分です。最後に、間隔の最大値と最小値を維持する必要があります。途中で操作をスキップする場合、後者への影響は、この期間を差し引くことに他なりません。xxxの影響は何もありません。

#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(nlogn)O(nlogn)ですO n l o g n レベル、公式の問題はO(n)O(n)O n レベルの
考え方は同じですが、メンテナンスについて考える必要があります。pr[i] pr [i]の使用を検討してください。p r [ i ]は、現在iiに到達していることを意味しますi桁の数字は何ですか、prl [i]、prr [i] prl [i]、prr [i]p r l [ i ] p r r [ i ]是前iiiの長さは最小、最大で、メンテナンスは非常に簡単です
。prl[i] = min(prl [i − 1]、pr [i] + d)prr [i] = max(prr [i − 1]、pr [i] + d)prl [i] = min(prl [i-1]、pr [i] + d)\\ prr [i] = max(prr [i-1]、pr [i ] + d)p r l [ i ]=m i n p r l [ i1 ] p r [ i ]+d p r r [ i ]=m a x p r r [ i1 ] p r [ i ]+d
しかし、接尾辞は少し注意が必要です。すでにsul [i] sul [i]を知っている場合は、そのようなことを検討してくださいs u l [ i ]iiからを意味しますiから始まる最小値、次にi − 1 i-11桁の場合、sul [i − 1] = sul [i] + d sul [i-1] = sul [i] + dである必要があります。s u l [ i1 ]=s u l [ i ]+d、正の数の場合は11です。1、次に00に変更します0で十分です。この式が当てはまらない場合は、最小ではなく、大きくすることしかできません。最大の場合も同様です。
最後に答えを維持するときは、pr [l − 1] pr [l-1]を直接維持してくださいp r [ 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