Gym-100803 Problem G Flipping Parentheses

题目链接

原文链接

题意:
先给出一个符合括号匹配的字符串,然后Q次操作

每次操作将某个括号反转,问将哪个括号反转能使字符串的括号再次匹配,位置要越靠近左端越好

做法:
假如(表示数字1,)表示数字-1,A[i]表示前i个括号代表的数字之和

((()))代表的数组A就是1 2 3 2 1 0

那么这些数字会有什么用呢,,我们可以来找下规律

  • 如果是将(变成),如上面那个字符串的第2个反转,就会得到()())),对应的A数组是1 0 1 0 -1 -2

    可以发现,反转后是以前的数组,在反转的位置t到n整个区间段的数字都减少了2

  • 如果将)变成(,同理会得到,反转后在反转的位置到n整个区间段的数字都增加了2

所以我们可以得到一个这样的决策:

设反转的位置是t

  • 若位置t是(,找到第一个),将)变成(,因为会使后面所有的数字都增加2,所以这样一定是最优的

  • 若位置t是),找到一个位置p,使得[p+1,n]的所有数字都>=2,然后将p+1的括号反转,这样就会让后面的数字都减少2,就还原了

  • 第一种情况可以用set维护,也可以利用F=A[i]-l的值去维护,如果[1,i]区间内存在),那么A[i]-l会小于0

  • 第二种情况明显可以用线段树去维护最小值,然后通过最小值去定位。

反省:
1.字符’(‘,’)’映射到数字1,-1。

2.若位置t是(,找到第一个),将)变成(,因为会使后面所有的数字都增加2,所以这样一定是最优的。
原因:题意“问将哪个括号反转能使字符串的括号再次匹配,位置要越靠近左端越好”,越靠近左端越好,将)变成(,即-1变为1。

代码:

#include <stdio.h>
#include <algorithm>
using namespace std;

const int MX = 300005;

#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define root 1,n,1

char W[MX];
int MIN[MX << 2], F[MX << 2], col[MX << 2], A[MX];

void // 线段树维护最小值
push_up(int rt) {
    MIN[rt] = min(MIN[rt << 1], MIN[rt << 1 | 1]);
    F[rt] = min(F[rt << 1], F[rt << 1 | 1]);
}

void
push_down(int rt) {
    if(col[rt]) {
        col[rt << 1] += col[rt];
        col[rt << 1 | 1] += col[rt];
        MIN[rt << 1] += col[rt];
        MIN[rt << 1 | 1] += col[rt];
        F[rt << 1] += col[rt];
        F[rt << 1 | 1] += col[rt];
        col[rt] = 0;
    }
}

void
build(int l, int r, int rt) {
    col[rt] = 0;
    if(l == r) {
        MIN[rt] = A[l];
        F[rt] = A[l] - l;
        return;
    }

    int m = (l + r) >> 1;
    build(lson);
    build(rson);
    push_up(rt);
}

void
update(int L, int R, int d, int l, int r, int rt) {
    if(L <= l && r <= R) {
        MIN[rt] += d;
        F[rt] += d;
        col[rt] += d;
        return;
    }

    int m = (l + r) >> 1;
    push_down(rt);
    if(L <= m) update(L, R, d, lson);
    if(R > m)  update(L, R, d, rson);
    push_up(rt);
}

int  //查询第一个)
query_1(int l, int r, int rt) {
    if(l == r) {
        return l;
    }

    int m = (l + r) >> 1;
    push_down(rt);
    if(F[rt << 1] < 0) return query_1(lson);
    else return query_1(rson);
}

int  // 查询一个位置p,使[p+1,n]全部大于等于2
query_2(int l, int r, int rt) {
    if(l == r) {
        return l;
    }

    int m = (l + r) >> 1;
    push_down(rt);
    if(MIN[rt << 1 | 1] < 2) return query_2(rson);
    else return query_2(lson);
}

int
main() {
//    freopen("in.txt", "r", stdin);
    int n, Q, t;
    while(~scanf("%d%d", &n, &Q)) {
        scanf("%s", W + 1);  // W + 1的用法

        int sum = 0;
        for(int i = 1; i <= n; i++) {
            sum += W[i] == '(' ? 1 : -1;
            A[i] = sum;
        }
        build(root);

        while(Q--) {
            scanf("%d", &t);
            if(W[t] == '(') {
                W[t] = ')'; update(t, n, -2, root);
                t = query_1(root); W[t] = '('; update(t, n, 2, root);
            } else {
                W[t] = '('; update(t, n, 2, root);
                t = query_2(root) + 1; W[t] = ')'; update(t, n, -2, root);
            }
            printf("%d\n", t);
        }
    }
}

线段树:

线段树详解

用这个题的线段树模板,实现了以前看的博客的一个基础的例子。
区别:push_down()的写法有点不一样,即处理lazy[]的方式不一样?自己不太明白,继续努力看代码,思考。

#include<stdio.h>

const int MX = 300005;

#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define root 1,n,1

int F[MX << 2], col[MX << 2];
int A[MX], n;

void
push_up(int rt) {
    F[rt] = F[rt << 1] + F[rt << 1 | 1];
}

void
push_down(int rt) {
    if(col[rt]) {
        col[rt << 1] += col[rt];
        col[rt << 1 | 1] += col[rt];
        F[rt << 1] += col[rt];
        F[rt << 1 | 1] += col[rt];
        col[rt] = 0;
    }
}

void
build(int l, int r, int rt) {
    col[rt] = 0;
    if(l == r) {
        F[rt] = A[l];
        return;
    }

    int m = (l + r) >> 1;
    build(lson);
    build(rson);
    push_up(rt);
}

void
update(int L, int R, int d, int l, int r, int rt) {
    if(L <= l && r <= R) {
        F[rt] += d;
        col[rt] += d;
        return;
    }

    int m = (l + r) >> 1;
    push_down(rt);
    if(L <= m) update(L, R, d, lson);
    else  update(L, R, d, rson);
    push_up(rt);
}

int
query(int L, int R, int l, int r, int rt) {
    if( L <= l && r <= R ) {
        return F[rt];
    }

    int m = (l + r) >> 1;

    push_down(rt);

    int ans = 0;

    if( L <= m ) {
        ans += query(L, R, l, m, rt << 1);
    }
    if( R > m ) {
        ans += query(L, R, m + 1, r, rt << 1 | 1);
    }
    return ans;
}

int
main() {
    freopen("in.txt", "r", stdin);

    int i, ans;

    scanf("%d", &n);
    for( i = 1; i <= n; i++ ) {
        scanf("%d", &A[i]);
    }
    build(1, n, 1);
    ans = query(2, 12, 1, n, 1);
    printf("ans: %d\n", ans);
    update(2, 12, 2, 1, n, 1);
    ans = query(2, 12, 1, n, 1);
    printf("ans: %d\n", ans);
}
/*
12
1 2 3 4 5 6 7 8 9 10 11 12

77
79
该区间修改只是区间的值+value,并不是区间内的每一个值+value。
*/
#include <stdio.h>

#define MAX 100005

int sum[MAX << 2], lazy[MAX << 2];
int a[MAX], n;

void
PushUp(int rt) {
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}

void
PushDown(int rt, int ln, int rn) {
    if( lazy[rt] ) {
        lazy[rt << 1] += lazy[rt];
        lazy[rt << 1 | 1] += lazy[rt];
        sum[rt << 1] += lazy[rt] * ln;
        sum[rt << 1 | 1] += lazy[rt] * rn;
        lazy[rt] = 0;
    }
}

void
Build(int l, int r, int rt) {
    if( l == r ) {
        sum[rt] = a[l];
        return ;
    }
    int m = (l + r ) >> 1;

    Build(l, m, rt << 1);
    Build(m + 1, r, rt << 1 | 1);
    PushUp(rt);
}

void
Update(int L, int v, int l, int r, int rt) {
    if( l == r ) {
        sum[rt] += v;
        return ;
    }
    int m = (l + r) >> 1;

    if( L <= m ) {
        Update(L, v, l, m, rt << 1);
    }
    else {
        Update(L, v, m + 1, r, rt << 1 | 1);
    }
    PushUp(rt);
}

void
Update(int L, int R, int v, int l, int r, int rt) {
    if( L <= l && r <= R ) {
        sum[rt] += v * (r - l + 1);
        lazy[rt] += v;
        return ;
    }
    int m = (l + r) >> 1;

    PushDown(rt, m - l + 1, r - m);

    if( L <= m ) {
        Update(L, R, v, l, m, rt << 1);
    }
    else {
        Update(L, R, v, m + 1, r, rt << 1 | 1);
    }
    PushUp(rt);
}

int
Query(int L, int R, int l, int r, int rt) {
    if( L <= l && r <= R ) {
        return sum[rt];
    }
    int m = (l + r) >> 1;

    PushDown(rt, m - l + 1, r - m);

    int ans = 0;

    if( L <= m ) {
        ans += Query(L, R, l, m, rt << 1);
    }
    if( R > m ) {
        ans += Query(L, R, m + 1, r, rt << 1 | 1);
    }
    return ans;
}

int
main() {
    freopen("in.txt", "r", stdin);

    int i, ans;

    scanf("%d", &n);
    for( i = 1; i <= n; i++ ) {
        scanf("%d", &a[i]);
    }
    Build(1, n, 1);
    ans = Query(2, 12, 1, n, 1);
    printf("ans: %d\n", ans);
    Update(2, 12, 2, 1, n, 1);
    ans = Query(2, 12, 1, n, 1);
    printf("ans: %d\n", ans);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值