UVALive 6838 Flipping Parentheses(线段树、单点更新、区间查询)
2016-04-17 23:32
429 查看
题目链接:
UVALive 6838 Flipping Parentheses
题意:
给出一个长度为n个串,每个字符只能是’(‘或’)’,而且左括号和右括号个数相等,在操作的过程中,要保证这个串的任意前缀串的左括号个数都要大于等于右括号的个数。
有Q个操作。每次操作输入一个坐标t将(坐标从1~n)这个坐标下的括号取反,即左括号变右括号,右括号变左括号。
对每次输入输出最靠前的一个坐标,使得改变这个坐标下的字符(取反),仍然满足任意前缀子串的左括号个数大于等于右括号个数?
分析:
将左括号个数减去右括号个数设为平衡度。显然整个串的平衡度为0,而且任意前缀串的平衡度大于等于0.
假设将id位置的括号从左变成右,那么从这个位置一直到串尾的每个位置上的前缀串的平衡度都增加2,相反,如果由右括号变成左括号,那么从这个位置一直到串尾的每个位置上的前缀串的平衡度都减少2.
首先考虑将第t位置的括号由左变右的情况,。显然需要找到最靠前的右括号将其变为左括号,假设坐标为id,如何找到id呢?可以在线段树中保存当前区间的右括号的个数,那么就相当于单点查询了。找到id后还需注意,因为我们需要的是操作完成后最靠前的右括号,如果id>t,那么因为需要将t位置的左括号改为右括号,那么这是id应该就是t,先变成右括号,再变成左括号。然后就是区间更新,更新从id位置到串尾的线段树区间最小前缀平衡度。
为什么要保存区间最小前缀平衡度呢?是因为接下来将第t位置的括号由右变左需要用到。我们需要找到的是一个左括号,同样假设下标id,将其变为右括号,而且保持任意前缀
子串的平衡度非负,这是我们应该先更新在查找。如果从前往后找的话,那么从id以后的子串的前缀平衡度就难以保证了。所以我们需要从后往前找。
找到一个这样的最靠前的位置:从这个位置往后(包括这个位置)的前缀平衡度恒大于等于2,因为左括号变右相当于平衡度减2.也就是从后往前找到第一个前缀平衡度小于2的后一个位置就是我们要随着t改变的位置。
更新区间前缀平衡度最小值要用lazy标记。
还有一个特例需要注意。就是要改变的位置是第一个括号和最后一个括号时,只能是在把它变回来。
UVALive 6838 Flipping Parentheses
题意:
给出一个长度为n个串,每个字符只能是’(‘或’)’,而且左括号和右括号个数相等,在操作的过程中,要保证这个串的任意前缀串的左括号个数都要大于等于右括号的个数。
有Q个操作。每次操作输入一个坐标t将(坐标从1~n)这个坐标下的括号取反,即左括号变右括号,右括号变左括号。
对每次输入输出最靠前的一个坐标,使得改变这个坐标下的字符(取反),仍然满足任意前缀子串的左括号个数大于等于右括号个数?
分析:
将左括号个数减去右括号个数设为平衡度。显然整个串的平衡度为0,而且任意前缀串的平衡度大于等于0.
假设将id位置的括号从左变成右,那么从这个位置一直到串尾的每个位置上的前缀串的平衡度都增加2,相反,如果由右括号变成左括号,那么从这个位置一直到串尾的每个位置上的前缀串的平衡度都减少2.
首先考虑将第t位置的括号由左变右的情况,。显然需要找到最靠前的右括号将其变为左括号,假设坐标为id,如何找到id呢?可以在线段树中保存当前区间的右括号的个数,那么就相当于单点查询了。找到id后还需注意,因为我们需要的是操作完成后最靠前的右括号,如果id>t,那么因为需要将t位置的左括号改为右括号,那么这是id应该就是t,先变成右括号,再变成左括号。然后就是区间更新,更新从id位置到串尾的线段树区间最小前缀平衡度。
为什么要保存区间最小前缀平衡度呢?是因为接下来将第t位置的括号由右变左需要用到。我们需要找到的是一个左括号,同样假设下标id,将其变为右括号,而且保持任意前缀
子串的平衡度非负,这是我们应该先更新在查找。如果从前往后找的话,那么从id以后的子串的前缀平衡度就难以保证了。所以我们需要从后往前找。
找到一个这样的最靠前的位置:从这个位置往后(包括这个位置)的前缀平衡度恒大于等于2,因为左括号变右相当于平衡度减2.也就是从后往前找到第一个前缀平衡度小于2的后一个位置就是我们要随着t改变的位置。
更新区间前缀平衡度最小值要用lazy标记。
还有一个特例需要注意。就是要改变的位置是第一个括号和最后一个括号时,只能是在把它变回来。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <climits> #include <cmath> #define lson(x) (x<<1) #define rson(x) ((x<<1)|1) using namespace std; const int MAX_N=300010; int n,Q; char s[MAX_N]; int sum[MAX_N]; struct Segtree{ int left,right; int num1,num2,balance_min,add; }segtree[MAX_N<<2]; inline void push_up(int cur) { segtree[cur].num1=segtree[lson(cur)].num1+segtree[rson(cur)].num1; segtree[cur].num2=segtree[lson(cur)].num2+segtree[rson(cur)].num2; segtree[cur].balance_min=min(segtree[lson(cur)].balance_min,segtree[rson(cur)].balance_min); } inline void push_down(int cur) { int add=segtree[cur].add; if(add!=0){ segtree[lson(cur)].balance_min+=add; segtree[rson(cur)].balance_min+=add; segtree[lson(cur)].add+=add; segtree[rson(cur)].add+=add; segtree[cur].add=0; return ; } } inline void build(int left,int right,int cur) { segtree[cur].left=left; segtree[cur].right=right; segtree[cur].add=0; if(left==right){ segtree[cur].balance_min=sum[left]; int val=s[left]=='('?1:0; segtree[cur].num1=val; segtree[cur].num2=1-val; return ; } int mid=(left+right)>>1; build(left,mid,lson(cur)); build(mid+1,right,rson(cur)); push_up(cur); } inline int query1(int a,int b,int cur) {//查询第一个右括号 int left=segtree[cur].left; int right=segtree[cur].right; if(left==right){ //printf("query1=%d\n",left); return left; } push_down(cur); int tmp=segtree[lson(cur)].num2; int mid=(left+right)>>1; int res; if(tmp) res=query1(left,mid,lson(cur)); //左儿子有右括号 else res=query1(mid+1,right,rson(cur)); push_up(cur); return res; } inline void update_dandian(int cur,int id,int flag) {//flag=0单点左改右,flag=1单点右改左 int left=segtree[cur].left; int right=segtree[cur].right; if(left==right){ //printf("update1_dandian:left==%d\n",left); segtree[cur].num1=flag; segtree[cur].num2=1-flag; return; } push_down(cur); int mid=(left+right)>>1; if(id<=mid) update_dandian(lson(cur),id,flag); else update_dandian(rson(cur),id,flag); push_up(cur); } inline void update_qujian(int a,int b,int cur,int flag) { int left=segtree[cur].left; int right=segtree[cur].right; if(left==a&&right==b){ segtree[cur].add+=flag; segtree[cur].balance_min+=flag; return ; } push_down(cur); int mid=(left+right)>>1; if(a>mid) update_qujian(a,b,rson(cur),flag); else if(b<=mid) update_qujian(a,b,lson(cur),flag); else { update_qujian(a,mid,lson(cur),flag); update_qujian(mid+1,b,rson(cur),flag); } push_up(cur); } inline int query2(int cur) { int left=segtree[cur].left; int right=segtree[cur].right; if(left==right){ return left; } push_down(cur); int mid=(left+right)>>1; int res; int tmp1=segtree[lson(cur)].balance_min; int tmp2=segtree[rson(cur)].balance_min; //printf("tmp1=%d tmp2=%d left=%d right=%d\n",tmp1,tmp2,left,right); if(tmp2<2) res=query2(rson(cur)); //从右往前找到第一个前缀平衡度小于2的位置 else res=query2(lson(cur)); push_up(cur); return res; } int main() { freopen("Gin.txt","r",stdin); while(~scanf("%d%d",&n,&Q)){ scanf("%s",s+1); memset(sum,0,sizeof(sum)); for(int i=1;i<=n;i++){ sum[i]=sum[i-1]+(s[i]=='('?1:(-1)); } build(1,n,1); for(int i=0;i<Q;i++){ int t; scanf("%d",&t); if(s[t]=='('){ //左改右 if(t==1){ printf("1\n"); continue; } int id=query1(1,n,1); //id是现有的第一个右括号位置 if(id>=t){ printf("%d\n",t); continue; } s[id]='('; update_dandian(1,id,1);//单点更新,右改左 update_qujian(id,n,1,2);//区间更新,右改左 s[t]=')'; update_dandian(1,t,0);//单点更新,左改右 update_qujian(t,n,1,-2);//区间更新,左改右 printf("%d\n",id); }else { //右改左 if(t==n){ printf("%d\n",n); continue; } update_dandian(1,t,1);//单点更新,右改左 update_qujian(t,n,1,2);//区间更新,右改左 s[t]='('; int id=query2(1)+1; update_dandian(1,id,0);//单点更新,左改右 update_qujian(id,n,1,-2);//区间更新,左改右 if(id>t) id=t; s[id]=')'; printf("%d\n",id); } } } return 0; }