Codeforces Round #603 (Div. 2), problem: (E) Editor

題目大意

給你一連串的操作,L, R分別代表移動光標操作,其他字符表示在當前光標位置插入一個字符。如果該位置有字符,那麼進行替換。讓你判斷當前輸出的序列是否合法,如果合法,那麼括號的最大嵌套深度是多少?

思路

首先這個序列的長度不會超過10610^6的長度,對於(我們可以看成在該位置加1,對於)我們可以看成在該位置減一,然後我們對該序列求前綴和,因爲需要保證輸出的括號序列合法,那麼需要判斷前綴和中是否有小於0的數以及整個序列的前綴和是否等於0,如果有,該序列不合法。除此之外。除此之外我們還需要維護前綴和的最大值,因爲最大值對應着括號的最大嵌套深度。
於是,這就是個線段樹的 區間更新的題目,線段樹的每個點維護前綴和。對於每次更改根據需要更新[當前光標,n]裏面所有前綴和的最大值和最小值。

代碼1

#include <bits/stdc++.h>
using namespace std;
#define mid (L+R)/2
#define lson o<<1, L, mid
#define rson o<<1|1, mid+1, R
const int maxn = 1e6+100;
 
struct SegmentTree{
    int pmin[maxn<<2], pmax[maxn<<2], lazy[maxn<<2];
 
    void pushdown(int o){
        if(lazy[o]){
            lazy[o<<1] += lazy[o], lazy[o<<1|1] += lazy[o];
            pmax[o<<1] += lazy[o], pmin[o<<1] += lazy[o];
            pmax[o<<1|1] += lazy[o], pmin[o<<1|1] += lazy[o];
            lazy[o] = 0;
        }
    }
 
    void pushup(int o){
        pmax[o] = max(pmax[o<<1], pmax[o<<1|1]);
        pmin[o] = min(pmin[o<<1], pmin[o<<1|1]);
    }
 
    void update(int o, int L, int R, int l, int r, int val){
        if(L >= l && R <= r){
            lazy[o] += val, pmax[o] += val, pmin[o] += val;
            return;
        }
        pushdown(o);
        if(l <= mid) update(lson, l, r, val);
        if(r > mid) update(rson, l, r, val);
        pushup(o);
    }
}tree;
 
char s[maxn], now[maxn];
int ans[maxn];
 
int main(){
    int n; scanf("%d %s", &n, s);
    int cursor = 1, sum = 0;
    for(int i = 0; i < n; i++){
        if(s[i] == 'R') cursor++;
        else if(s[i] == 'L') cursor = max(cursor-1, 1);
        else{
            if(s[i] == '('){
                if(now[cursor] == ')') sum += 2, tree.update(1, 1, n, cursor, n , 2);
                else if(now[cursor] == '(');
                else sum++, tree.update(1, 1, n, cursor, n, 1);
            }
            else if(s[i] == ')'){
                if(now[cursor] == ')') ;
                else if(now[cursor] == '(') sum -= 2, tree.update(1, 1, n, cursor, n, -2);
                else sum--, tree.update(1, 1, n, cursor, n, -1);
            }
            else{
                if(now[cursor] == ')') sum++, tree.update(1, 1, n, cursor, n, 1);
                else if(now[cursor] == '(') sum--, tree.update(1, 1, n, cursor, n, -1);
            }
            now[cursor] = s[i];
        }
        if(sum == 0 &&  tree.pmin[1] >= 0) ans[i] = tree.pmax[1];
        else ans[i] = -1;
    }
    for(int i = 0; i < n; i++) printf("%d ", ans[i]);
    return 0;
}

supplement

上一部分的代碼即臃腫有複雜。還有更簡便的方法。我們發現可以將區間更新轉化爲單點更新。每個點的sum值表示當前區間和,而不是前綴和。pmin和pmax數組也表示當前區間的前綴和的最小值和最大值,而不是從1到當前位置的前綴和。
那麼如何進行區間並的操作和呢?具體操作如下:

pmin[o] = min(pmin[o<<1], psum[o<<1] + pmin[o<<1|1]);
pmax[o] = max(pmax[o<<1], psum[o<<1] + pmax[o<<1|1]);
psum[o] = psum[o<<1] + psum[o<<1|1];

因爲不用考慮那麼多情況直接處理到葉子結點,所以整個代碼被壓縮的很短。
代碼如下:

#include <bits/stdc++.h>
using namespace std;
#define mid (L+R)/2
#define lson o<<1, L, mid
#define rson o<<1|1, mid+1, R
const int maxn = 1e6+100;
 
struct SegmentTree{
    int psum[maxn<<2], pmin[maxn<<2], pmax[maxn<<2];
 
    void pushup(int o){
        pmin[o] = min(pmin[o<<1], psum[o<<1] + pmin[o<<1|1]);
        pmax[o] = max(pmax[o<<1], psum[o<<1] + pmax[o<<1|1]);
        psum[o] = psum[o<<1] + psum[o<<1|1];
    }
 
    void update(int o, int L, int R, int p, char val){
        if(L == R){
            if(val == '(') psum[o] = pmin[o] = pmax[o] = 1;
            else if(val == ')') psum[o] = pmin[o] = -1, pmax[o] = 0;
            else psum[o] = pmin[o] = pmax[o] = 0;
            return ;
        }
        if(p <= mid) update(lson, p, val);
        else update(rson, p, val);
        pushup(o);
    }
}tree;
 
char s[maxn];
int ans[maxn];
 
int main(){
    int n; scanf("%d %s", &n, s);
    int cursor = 1;
    for(int i = 0; i < n; i++){
        if(s[i] == 'R') cursor++;
        else if(s[i] == 'L') cursor = max(1, cursor-1);
        else tree.update(1, 1, n, cursor, s[i]);
        if(tree.psum[1] == 0 && tree.pmin[1] >= 0) ans[i] = tree.pmax[1];
        else ans[i] = -1;
    }
    for(int i = 0; i < n; i++) printf("%d ", ans[i]);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章