1. 程式人生 > >Flipping Parentheses (線段樹 單點更新 區間查詢)

Flipping Parentheses (線段樹 單點更新 區間查詢)

題目大意 : 給一個括號已經匹配好的序列,每次反轉一個括號, 然後讓你再次反轉一個括號再次使得括號匹配,並且你反轉的位置儘可能的靠近左端。

可以對於每個位置,記錄它之前的左括號的數量減去右括號的數量,這個序列合法的條件就是每個位置不會出現值小於0的情況。最後一位一定是0.

如果他反轉的是右括號,這個位置變成左括號之後 你需要找到一個左括號把它反成右括號。而你需要找的位置就是從最後一個往前最後一個為大於等於2的位置。

如果他反轉的是左括號,那就找到最左邊的右括號。

兩種情況分別都可以用線段樹維護

#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#define MAX 0x3f3f3f3f
#define N 300005
#define mod 1000000007
#define lson o<<1, l, m
#define rson o<<1|1, m + 1, r
typedef long long LL;
const double pi = acos(-1.0);
using namespace std;

int n, q, X, A, B, mi[N<<2], add[N<<2];
bool E, v[N<<2];
char s[N];

void down(int o) {
    if(add[o]) {
        add[o<<1] += add[o];
        add[o<<1|1] += add[o];
        mi[o<<1] += add[o];
        mi[o<<1|1] += add[o];
        add[o] = 0;
    }
}

void modify(int o, int l, int r) {
    if(l == r) v[o] = E;
    else {
        int m = (l+r) >> 1;
        if(X <= m) modify(lson);
        else modify(rson);
        v[o] = v[o<<1] | v[o<<1|1];
    }
}

void build(int o, int l, int r) {
    if(l == r) mi[o] = A;
    else {
        int m = (l+r) >> 1;
        if(X <= m) build(lson);
        else build(rson);
        mi[o] = min(mi[o<<1], mi[o<<1|1]);
    }
}

void update(int o, int l, int r) {
    if(X <= l && r <= n) {
        add[o] += B;
        mi[o] += B;
    } else {
        down(o);
        int m = (l+r) >> 1;
        if(X <= m) update(lson);
        if(m < n ) update(rson);
        mi[o] = min(mi[o<<1], mi[o<<1|1]);
    }
}

int Find(int o, int l, int r) {
    if(l == r) return l;
    else {
        int m = (l+r) >> 1;
        if(v[o<<1]) return Find(lson);
        else return Find(rson);
    }
}

int query(int o, int l, int r) {
    if(l == r) return mi[o] >= 2;
    else {
        down(o);
        int m = (l+r) >> 1, res = 0;
        if(mi[o<<1|1] >= 2) {
            res += r-m;
            res += query(lson);
        } else res += query(rson);
        return res;
    }
}

int main()
{
    //freopen("in.txt", "r", stdin);
    //read(n), read(q);
    scanf("%d%d", &n, &q);
    scanf("%s", s+1);
    int zuo = 0, you = 0;
    for(X = 1; X <= n; X++) {
        if(s[X] == '(') zuo++;
        else {
            you++;
            E = 1, modify(1, 1, n);
        }
        A = zuo - you;
        build(1, 1, n);
    }
    while(q--) {
        scanf("%d", &X);
        if(s[X] == '(') {
            s[X] = ')';         // 修改成右括號
            B = -2, update(1, 1, n);
            E = 1, modify(1, 1, n);

            X = Find(1, 1, n);   // 找到第一個右括號的位置
            printf("%d\n", X);

            s[X] = '(';         // 將它改為左括號
            B = 2, update(1, 1, n);
            E = 0, modify(1, 1, n);
        } else {
            s[X] = '(';        //修改成左括號
            B = 2, update(1, 1, n);
            E = 0, modify(1, 1, n);

            X = n - query(1, 1, n) + 1;  // 找到滿足條件的最左邊的左括號
            printf("%d\n", X);

            s[X] = ')';   //將它修改成右括號
            B = -2, update(1, 1, n);
            E = 1, modify(1, 1, n);
        }
    }

    return 0;
}