【线段树】JZOJ_7.12C组第二题 圣章-精灵使的魔法语

题意

给出一个字符串,里面都是’(‘或者’)’,左边的’(‘可以匹配右边的’)’,但是像)(这样的就不匹配了。现在有2种操作:
1、Change x:表示把字符串里的第x位进行修改,如果是’(‘就改成’)’,如果是’)‘就改成’(’。
2、Query a b:询问在a~b这个区间中,我们在最左边至少要加多少’(‘和在最右边至少加上多少’)'可以使得这个区间内的括号是都匹配的。

思路

刚开始打了个水法,但是还是没拿到40分,正解是线段树。区间查询和单点修改这两个刚好可以用线段树来做,我们维护两个值ln和rn,分别表示当前当前区间还多余的’(‘和需要的’(’。

代码

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define T 800001
using namespace std;
int n,m,ans1,ans2,a,b,w;
char c[150001],p;
int ln[T],rn[T],l[T],r[T];
void read(int &tot)
{
    tot=0;
	char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) tot=tot*10+c-48,c=getchar();
}
void build(int x,int left,int right)
{
	l[x]=left;r[x]=right;
	if (left==right)
	{
		ln[x]=(c[left]=='(')?1:0;//ln是多余的'('
		rn[x]=1-ln[x];//rn是需要的'('
		return;
	}
	int mid=(left+right)>>1;
	build(2*x,left,mid);
	build(2*x+1,mid+1,right);
	ln[x]=ln[2*x+1]+max(ln[2*x]-rn[2*x+1],0);//左子树的'('只能消掉右子树的')',所以是
	         //ln[2*x]-rn[2*x-1],和0取最大值是有可能出现左子树的'('不够消掉右子树的')'的情况 
	rn[x]=rn[2*x]+max(rn[2*x+1]-ln[2*x],0);//右子树的需要的'('只能被左子树的'('消掉,所以是
			 //rn[2*x+1]-ln[2*x],和0取最大值是有可能左子树的'('能把右子树的')'全部消掉 
}
void query(int x,int left,int right)
{
	if (l[x]==left&&r[x]==right)
	{
		ans1+=max(rn[x]-ans2,0); 
		ans2=ln[x]+max(ans2-rn[x],0);
		return;
	}
	int mid=(l[x]+r[x])>>1;
	if (right<=mid) query(x*2,left,right);
	else if(left>mid) query(x*2+1,left,right);
	else 
	{
		query(x*2,left,mid);
		query(x*2+1,mid+1,right);
	}
}
void change(int x,int now)
{
	if (l[x]==r[x]) 
	{
		ln[x]=ln[x]==1?0:1;//取反
		rn[x]=1-ln[x];
		return;
	}
	int mid=(l[x]+r[x])>>1;
	if (now>mid) change(x*2+1,now);
	else if (now<=mid) change(x*2,now);
	ln[x]=ln[2*x+1]+max(ln[2*x]-rn[2*x+1],0);
	rn[x]=rn[2*x]+max(rn[2*x+1]-ln[2*x],0);//在修改单点的时候顺便更新修改过后的ln,rn 
}
int main()
{
	freopen("elf.in","r",stdin);
	freopen("elf.out","w",stdout);
	scanf("%d%d",&n,&m);
	scanf("%s",c+1);
	build(1,1,n);
	for (int i=1;i<=m;i++)
	{
		while (scanf("%c",&p),p!='C'&&p!='Q');//只用读一个字符就能判断,后面的不用管,read可以全部过滤掉 
		if (p=='Q') 
		{
			ans1=0;ans2=0;
			read(a);
			read(b);
			query(1,a,b);//查询 
			printf("%d %d\n",ans1,ans2);
		}
		else
		{
			read(w);
			change(1,w);//修改 
		}
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值