UVALive 6838

更改括号与括号匹配

题目描述:

给出一个已经匹配好的(()),长度n~300000, 下面给出Q~150000次修改,每行一个x,每次把x处的东西反转。要求对于每个修改,找出尽量靠左的东西修改y,把y处的反转,使得仍然匹配,输出y。并且随着修改是累积的

题解:

其实是线段树的操作。对于括号,(相当于1,)相当于-1,因为最开始是匹配好的,因此满足匹配只要:从头到尾的和均》=0就好了。那么强制改①(到),会从x到len都-2,之后只需找到最靠左的),改成(就行了,我是用一个set模拟平衡树记录右括号的index,删除的时候是指定的index,所以log,而寻找是begin。②)到(,会从x到len都+2,之后要找从x往前第一个小于2的再加一就是y的位置。并且此时从y到len都是》=2的,因此可以用线段树天生的log分查询第一个小于2的位置。然后就都解决了

重点:

首先思考平时括号匹配的过程,搞出前缀和,然后发现用线段树和set解决的策略。然后是线段树的查询,对于②不能够loglog的二分查询,应该直接log在线段树上跑。

代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(int i = a;i < b;i++)
#define REP_D(i, a, b) for(int i = a;i <= b;i++)

typedef long long ll;

using namespace std;

const int maxn = 3e5 + 100;
const int MAXN_NODE = 4*maxn + 10;//线段树的定点是4倍
const int INF = 0;
char s[maxn];
int n, a[maxn], m;
//set<int> lft;
set<int> rgt;//模拟平衡树

int tree[MAXN_NODE], late[MAXN_NODE];
int tN;

void pushUp(int rt)//下面的都已经好了.且一定有下面的
{
    int lRt = (rt<<1), rRt = ((rt<<1)|1);
    tree[rt] = min(tree[lRt], tree[rRt]);
}
void getTree(int rt)//利用标准的tN来初始化线段树
{
    if(rt>=tN)//注意,因为要取最小值,所以很多多余的地方要置最大值
    {
        if(rt-tN + 1 <= n)
            tree[rt] = a[rt - tN + 1];
        else
            tree[rt] = 1e9;
    }
    else
    {
        int lRt = (rt<<1), rRt = ((rt<<1)|1);//不是最后一层的要pushUp
        getTree(lRt);
        getTree(rRt);
        pushUp(rt);
    }
}
void initail()
{
    CLR(tree);
    CLR(late);
    getTree(1);
}

void pushDown(int rt)//向下push,此延迟到下一层延迟.
{
    if(late[rt] == INF)//不用
        return;
    int lRt = (rt<<1), rRt = ((rt<<1)|1);
    int key = late[rt];//注意修改,但是tree就不用再次更新
    late[rt] = INF;
    late[lRt] += key;//不要忘了原来也有值
    tree[lRt] += key;
    late[rRt] += key;
    tree[rRt] += key;
}


void change(int L, int R, int key, int rt, int l, int r)
{
    if(L <= l && R >= r)//主义同时修改tree
    {
        late[rt] += key;
        tree[rt] += key;
        return;
    }
    pushDown(rt);
    int mid = ((l + r)>>1), lRt = (rt<<1), rRt = ((rt<<1)|1);
    if(L <= mid)
    {
        change(L, R, key, lRt, l, mid);
    }
    if(R >= mid + 1)
    {
        change(L, R, key, rRt, mid + 1, r);
    }
    pushUp(rt);//向上push
}

int query(int L, int R, int rt, int l, int r)
{
    if(L <= l && R >= r)//全包括
    {
        return tree[rt];
    }
    pushDown(rt);//下一次层延迟
    int ans = 1e8, mid = ((l + r)>>1), lRt = (rt<<1), rRt = ((rt<<1)|1);
    if(L <= mid)
    {
        ans = min(ans, query(L, R, lRt, l, mid));
    }
    if(R >= mid + 1)
    {
        ans = min(ans, query(L, R, rRt, mid + 1, r));
    }
    return ans;
}
int query_2(int rt, int l, int r)//本题中线段树的关键。查询最靠右的第一个《2的出现位置。从rt为1的自动二分
{
    if(l==r)
    {
        return l;
    }
    pushDown(rt);//下一次层延迟
    int mid = ((l + r)>>1), lRt = (rt<<1), rRt = ((rt<<1)|1);
    if(tree[rRt] < 2)
    {
        return query_2(rRt, mid + 1, r);
    }
    return query_2(lRt, l, mid);
}
int b_search(int x)//后来没用到,这个是loglog的
{
    int l = 1, r = x;
    while(l < r)
    {
        int mid = (l + r)/2;
        int key = query(mid, x, 1, 1, tN);
        if(key>=2)
        {
            r = mid;
        }
        else
        {
            l = mid + 1;
        }
    }
    return r;
}

void getPre()
{
    //lft.clear();
    rgt.clear();
    n = strlen(s+1);
    tN = 1;
    while(tN < n)//标准tN的写法
    {
        tN *= 2;
    }
    a[0] = 0;
    REP_D(i, 1, n)
    {
        if(s[i]=='(')
        {
            a[i] = a[i - 1] + 1;
            //lft.insert(i);
        }
        else
        {
            a[i] = a[i - 1] - 1;
            rgt.insert(i);
        }
    }
    initail();
    REP_D(i, 1, m)
    {
        int x;
        scanf("%d", &x);
        if(s[x]=='(')
        {
            s[x] = ')';
            change(x, n, -2, 1, 1, tN);
            //lft.erase(x);
            rgt.insert(x);
            int y = *(rgt.begin());
            printf("%d\n", y);
            s[y] = '(';
            rgt.erase(y);
            //lft.insert(y);
            change(y, n, 2, 1, 1, tN);
        }
        else
        {
            //printf("---------  %d\n", x);
            s[x] = '(';
            change(x, n, 2, 1, 1, tN);
            //lft.insert(x);
            rgt.erase(x);
            //int y;
            int y = query_2(1, 1, tN) + 1;
            //int y = b_search(x);
            //printf("yy is %d  y is %d\n", yy, y);
            //y = *(lft.lower_bound(y));
            printf("%d\n", y);
            s[y] = ')';
            rgt.insert(y);
            //lft.erase(y);
            change(y, n, -2, 1, 1, tN);
        }
    }
}

int main()
{
   //freopen("7Gin.txt", "r", stdin);
    //freopen("7Gout.txt", "w", stdout);
    while(scanf("%d%d", &n, &m) != EOF)
    {
        scanf("%s", s + 1);
        getPre();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值