CSUOJ 1542 线段树解决括号反向问题

题目大意:

根据初始给定的合法的小括号排序,每次进行一个操作,将第a位的括号反向,找到一个尽可能靠前的括号反向后是整个括号排列合法

 

数据量十分大,不断进行查询,要用线段树进行logn的复杂度的查询

首先最简单的考虑 '('->')' ,  稍微想一下可以知道因为要尽可能靠前,所以其实把最前面的那个 )改成 ( 即可,这里我就用 minn[] 数组记录区间内最早出现的 ) 的下标

然后是考虑 ')'->'(' , 我们可以倒着字符串来看,从后往前每次出现一个 ) 都记录加1 , 那么每次遇到一个 ( 就抵消1 , 那么当遇到 (没东西抵消时,说明这个是离尾部最远的不合法的符号,离尾部最远,那么就可以理解为离起点最近

具体怎么写的话就是可以将 '(' 看作 1 , ')' 看作-1 , 利用一个数组 minsum[] 记录区间内前缀和的最小值

对于一个线段树来说如果那个符号的位置出现在左子树的区间上,那么右子树中的所有前缀和都必然 >=2

所以查询就很容易得到 if(sum[rs] < 2) ans = 右子树的查询,else ans = 左子树的查询

 

但再想想的话,返回的值不能是当前下标 , 而是下标+1;

因为根据前面所讲,你所需要修改的位置 i 是 i 后面的数相加和正好为0的

也就是说到达i的前缀之和正好是2

而 第 i-1 位的前缀和正好为1,每次不断判断找   < 2的点,那么最后查询到的是i-1

也就是必须要加个1才能到达我需要改的位置

  1 #include <cstdio>

  2 #include <cstring>

  3 #include <iostream>

  4 using namespace std;

  5  

  6 #define ls o<<1

  7 #define rs o<<1|1

  8 #define define_m int m=(l+r)>>1

  9 const int N = 300010;

 10 const int INF = 0x3f3f3f3f;

 11 int minn[N<<2] , minsum[N<<2] , a[N] , add[N<<2];

 12 char str[N];

 13  

 14 void push_up(int o)

 15 {

 16     minsum[o]=min(minsum[ls] , minsum[rs]);

 17     minn[o]=min(minn[ls] , minn[rs]);

 18 }

 19  

 20 void push_down(int o)

 21 {

 22     if(add[o]){

 23         add[ls]+=add[o];

 24         add[rs]+=add[o];

 25         minsum[ls]+=add[o];

 26         minsum[rs]+=add[o];

 27         add[o]=0;

 28     }

 29 }

 30  

 31 void build(int o , int l , int r)

 32 {

 33     add[o]=0;

 34     if(l == r) {

 35         minn[o] = str[l]==')'?l:INF;

 36         minsum[o]=a[l];

 37         return;

 38     }

 39     define_m;

 40     build(ls , l , m);

 41     build(rs , m+1 , r);

 42     push_up(o);

 43 }

 44  

 45 void update(int o , int l , int r , int s , int t , int v)

 46 {

 47     if(l>=s && r<=t){

 48         minsum[o]+=v;

 49         add[o]+=v;

 50         return;

 51     }

 52     push_down(o);

 53     define_m;

 54     if(m>=s) update(ls , l , m , s , t , v);

 55     if(m<t) update(rs , m+1 , r ,s , t , v);

 56     minsum[o]=min(minsum[ls] , minsum[rs]);

 57 }

 58  

 59 void update1(int o , int l , int r , int pos)

 60 {

 61     if(l == r && l == pos){

 62         minn[o] = (str[l]==')'?l:INF);

 63         return;

 64     }

 65     push_down(o);

 66     define_m;

 67     if(m >= pos) update1(ls , l , m ,pos);

 68     else update1(rs , m+1 , r , pos);

 69     minn[o] = min(minn[ls] , minn[rs]);

 70 }

 71  

 72 int query(int o , int l , int r , int n)

 73 {

 74    // cout<<"o: "<<o<<" l: "<<l<< " r: "<<r<<" left: "<<minsum[ls]<<" right: "<<minsum[rs]<<endl;

 75     if(l==r) return l+1;

 76     push_down(o);

 77     define_m;

 78     if(minsum[rs]<2) return query(rs , m+1 , r , n);

 79     else return query(ls , l , m , n);

 80 }

 81  

 82 int main()

 83 {

 84    // freopen("a.in" , "r" , stdin);

 85     int n , m , pos;

 86     while(scanf("%d%d" , &n , &m) != EOF)

 87     {

 88         scanf("%s" , str+1);

 89         for(int i=1 ; i<=n ; i++){

 90             a[i]=a[i-1]+(str[i]=='('?1:-1);

 91         }

 92         build(1 , 1 , n);

 93         int res;

 94         for(int i=0 ; i<m ; i++){

 95             scanf("%d" , &pos);

 96             if(str[pos] == '('){

 97                 str[pos] = ')';

 98                 update1(1 , 1 , n , pos);

 99                 update(1 , 1 , n , pos , n , -2);

100                 res = minn[1];

101                 str[res] = '(';

102                 update1(1 , 1 , n , res);

103             }

104             else{

105                 str[pos] = '(';

106                 update1(1 , 1 , n , pos);

107                 update(1 , 1 , n , pos , n , 2);

108                 res = query(1 , 1 , n , n);

109                 str[res] = ')';

110                 update1(1 , 1 , n , res);

111             }

112             update(1 , 1 , n , res , n , str[res] == '('?2:-2);

113             printf("%d\n" , res);

114         }

115     }

116     return 0;

117 }

 

你可能感兴趣的:(线段树)