您的位置:首页 > 其它

Flipping Parentheses

2015-05-22 08:02 141 查看
题目来源:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=138130输入:
6 3
((()))
4
3
1
输出:
2
2
1
题意:输入n,q (2<n<300000,1<q<150000)第二行再输入长度为n(下标从1开始)的字符串,只含‘(’和‘)’,且是匹配好的。再输入q个下标查询。问改变输入下标位置的字符,如‘(’-->‘)’ 或者‘)’->‘(’求改变一个字符,使字符串全部匹配(必须从左端找出一个)。例:
1  2  3  4  5  6
( ( (  ) ) )
输入4变成:
1  2  3  4  5 6
( (  (  (  ) )
改变下标为2的字符,变成:
1  2  3  4  5 6
(    )(  (  ) )
字符串又变成匹配的。
思路:
1、利用左+,右-,a
(1)第一种情况‘)’->'('
1    2   3   4   5   6 ( ( (  ) ) )
a:1 2 3 2 1 0
输入4变成:
1   2    3    4   5   6
(  (  (  (   )  )
a: 1    2    3   4   3   2
结论:从上图可以看出在改变值4开始每次都+2
(2)第二种情况‘(’->‘)’
  1    2   3   4   5   6 ( ( (  ) ) )
a:1 2 3 2 1 0
输入3变成:
1    2   3   4   5   6
( (   )  ) ) )
a:1 2 1 0 -1 -2
结论:从上图可以看出在改变值3开始每次都-2
2、每次查询,改变之后,整个字符串都更新了一遍。因此联想到利用线段树来查询,更新
3、查找能使整个字符串恢复到匹配状态。(通过验证,找规律)
(1)如果输入的是‘)’->'(',就要找从最左边查找,查找字符为‘(’的下标。
方法:
从最左边查找,当a数组>=2的位置时,就算已经找到了。
(2)如果输入的是‘(’->')',就要从最左边查找们第一次出现的‘)’。
  方法:
要算出f数组,f数组=a数据-下标;
从最左边查找,当f数组为>0,就算已经找到了。
#include<iostream>#include<algorithm>#include<cstring>#include<cstdio>using namespace std;const int MAXN=300010;char s[MAXN];int sum[MAXN];struct Node{int a,f,s;}tree[MAXN*4];int MIN(int x,int y) {return x>y?y:x;}int fun(int i){return s[i]=='('?1:-1;}void buildtree(int u,int L,int R)//建树{tree[u].s=0;if(L==R){tree[u].f=sum[L]-L;tree[u].a=sum[L];return ;}int mid=(L+R)/2;buildtree(u<<1,L,mid);buildtree((u<<1)+1,mid+1,R);tree[u].f=MIN(tree[u<<1].f,tree[(u<<1)+1].f);tree[u].a=MIN(tree[u<<1].a,tree[(u<<1)+1].a);}void get_down(int u)//延迟更新{tree[u<<1].s+=tree[u].s;tree[u<<1].a+=tree[u].s;tree[u<<1].f+=tree[u].s;tree[(u<<1)+1].s+=tree[u].s;tree[(u<<1)+1].a+=tree[u].s;tree[(u<<1)+1].f+=tree[u].s;tree[u].s=0;}void update(int u,int L,int R,int x,int add)//更新{if(x<=L){tree[u].s+=add;tree[u].f+=add;tree[u].a+=add;return ;}get_down(u);int mid=(L+R)/2;if(x<=mid) update(u<<1,L,mid,x,add);//左侧可能需要更新update((u<<1)+1,mid+1,R,x,add);//右侧是一定要更新的tree[u].f=MIN(tree[u<<1].f,tree[(u<<1)+1].f);tree[u].a=MIN(tree[u<<1].a,tree[(u<<1)+1].a);}//查询从len往前查询,最后一个<2的位置+1,也就是从左边第一个>=2 的位置int query1(int u,int L,int R){if(L==R) return L+1;int mid=(L+R)/2;get_down(u);if(tree[(u<<1)+1].a<2) query1((u<<1)+1, mid+1, R);else query1((u<<1),L,mid);}int query2(int u,int L,int R)//查询最左边第一次出现')'的位置{if(L==R) return L;int mid=(L+R)/2;get_down(u);if(tree[u<<1].f<0) query2(u<<1,L,mid);else query2((u<<1)+1,mid+1,R);}int main(){int i,t,n,m,x,y,len;while(scanf("%d%d\n",&t,&n)==2){s[0]='0';memset(sum,0,sizeof(sum));scanf("%s",s+1);len=strlen(s)-1;for(i=1;i<=len;i++)sum[i]=sum[i-1]+fun(i);buildtree(1,1,len);for(i=1;i <=n;i++){scanf("%d",&x);s[x]=(s[x]=='(' ? ')' : '(');if(s[x]=='(')  update(1,1,len,x,2);else  update(1,1,len,x,-2);if(s[x]=='(') y=query1(1,1,len);//>=2,找( -> )else y=query2(1,1,len); //找第一次出现')',)->(s[y]=(s[y]=='(' ? ')' : '(');if(s[y]=='(')  update(1,1,len,y,2);else  update(1,1,len,y,-2);printf("%d\n",y);}}return 0;}//1 2 3 4 5 6//( ( ( ) ) )//1 2 3 2 1 0//')'->'(' +2  4//( ( ( ( ) )//1 2 3 4 3 2//找第一次>=2    a//'('->')'-2 3//( (  )  )   )   )//1 2  1  0  -1  -2//0 0 -2 -4 -6  -8  f//找第一次')'
[/code]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: