牛客国庆集训派对Day1 - J Princess Principal (线段树+栈)

29 篇文章 0 订阅
8 篇文章 0 订阅

https://www.nowcoder.com/acm/contest/201/J

题意:

有m种括号,给你一个括号字符串,每次询问问你l到r合法不。

 

POINT:

注意:([)]这样的括号匹配是不合法的。

这样就很容易知道,每一个右括号如果能匹配,肯定是有唯一的左括号的。

如果只有一种括号,我们怎么判断他合法的呢:

遇左加入栈,遇右pop栈,如果栈空,则不合法。如果最后栈不空,则不合法。把左括号变为1,右括号为-1.

若要合法,也就是l到r的和为0,且在l到r的任意时刻,和都不能为负数,(即右括号比左括号多了)。

 

那么这题也差不多。遇左加入栈,遇右,看看栈顶的左括号和右括号匹不匹配,匹配,则把左右括号的相对位置上的值变为1和-1.

若不匹配,那么这个右括号已经无效了,而且只要l到r内包含这个右括号,必定使串不合法。

不是1和-1的全变成-inf

 

这样一个操作之后。我们得到了一个1 -1 -inf的串。

比如(())[(])[]  这个串为1 1 -1 -1 -inf -inf -inf -inf  1 -1。

然后我们查询l到r这个串合不合法,先只要看sum(l,r)==0,若等于0,若为0 ,则再看中途有没有变为负数:

这个怎么看,我们维护一个前缀和pre。 一段区间[L,R]的和是pre[R]-pre[L-1]。

那么我们只要找到[L,R]中最小的pre,如果这个最小的pre-pre[L-1]>=0的话,就证明中途没有变为负数。

这样这个区间就合法了。

找区间内最小值,RMQ或者线段树。这题RMQ比较快,因为没有修改操作。但我用了线段树

 

 

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stack>
using namespace std;
const int N = 1e6 + 1000;
#define LL long long
const LL inf = 0x3f3f3f3f3f3f3f3f;

LL sum[N];
int a[N];
LL Min[N<<2];
int b[N];
struct node
{
	int id ,val;
}stk[N];

int top=0;

void build(int x,int l,int r)
{
	if(l==r){
		Min[x]=sum[l];
		return;
	}
	int mid = (l+r)>>1;
	build(x<<1,l,mid);
	build(x<<1|1,mid+1,r);
	Min[x]=min(Min[x<<1],Min[x<<1|1]);
}

LL query(int x,int l,int r,int ll,int rr)
{
	if(ll<=l&&rr>=r){
		return Min[x];
	}else{
		LL ans=inf;
		int mid = (l+r)>>1;
		if(ll<=mid) ans=min(ans,query(x<<1,l,mid,ll,rr));
		if(mid<rr) ans=min(ans,query(x<<1|1,mid+1,r,ll,rr));
		return ans;
	}
}

int main()
{
	int n,m,q;
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		b[i]=-100000000;
	}
	for(int i=1;i<=n;i++){
		if(a[i]%2==0){
			node x;x.id=i;x.val=a[i];
			stk[++top]=x;
		}else{
			if(top>=1){
				if(stk[top].val%2==0&&stk[top].val+1==a[i]){
					b[stk[top].id]=1;
					b[i]=-1;
					top--;
				}else{
					top=0;
				}
			}
		}
	}
	for(int i=1;i<=n;i++)
		sum[i]=sum[i-1]+b[i];
	build(1,1,n);
	while(q--){
		int l,r;
		scanf("%d%d",&l,&r);
		if(sum[r]-sum[l-1]!=0||query(1,1,n,l,r)<sum[l-1]){
			printf("No\n");
		}else{
			printf("Yes\n");
		}
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值