[NOI online22提高A] 丹钓战

题目描述
\(n\) 个二元组 \((a_i, b_i)\),编号为 1 到 n。

有一个初始为空的栈 SS,向其中加入元素 \((a_i, b_i)\) 时,先不断弹出栈顶元素直至栈空或栈顶元素 \((a_j , b_j)\) 满足 \(a_i \neq a_j\)\(b_i < b_j\) ,然后再将其加入栈中。

如果一个二元组入栈后栈内只有这一个元素,则称该二元组是“成功的”。

\(q\) 个询问 \([l_i, r_i]\),表示若将编号在 \([l_i, r_i]\) 中的二元组按编号从小到大依次入栈,会有多少个二元组是“成功的”。

询问之间相互独立。

输入格式
第一行两个正整数 \(n,q\)

第二行 \(n\) 个正整数表示 \(a_i\)

第三行 \(n\) 个正整数表示 \(b_i\)

接下来 \(q\) 行,每行两个正整数 \(l_i, r_i\),表示一组询问。

输出格式\(q\) 行,每行一个自然数表示一组询问的答案。

输入输出样例
输入 #1

10 4
3 1 3 1 2 3 3 2 1 1
10 10 2 9 7 5 4 7 6 1
1 4
7 8
7 10
1 8

输出 #1

3
2
2
3

说明/提示
【样例解释】

以第一次询问 \([1, 4]\) 为例。

一开始栈为 \(\{\}\)

加入 \(1\) 号二元组后栈为 \(\{(3,10)\}\),栈中只有一个元素,该二元组是“成功的”。

加入 \(2\) 号二元组 \((1, 10)\) 时,栈顶的 \((3, 10)\) 的 bb 值不大于 22 号二元组的,因此弹栈。此时栈空,\(2\) 号二元组入栈,栈为 \(\{(1, 10)\}\),该二元组是“成功的”。

加入 \(3\) 号二元组 \((3,2)\),此时栈顶元素与之 \(a\) 值不同,\(b\) 值比它更大,因而不需要弹栈,直接将 \(3\) 号二元组入栈,栈为 \(\{(1, 10),(3, 2)\}\),栈中有多个元素,该二元组不是“成功的”。

加入 \(4\) 号二元组 \((1,9)\),此时栈顶元素 \((3, 2)\)\(b\) 值比它小,弹栈。弹栈后栈顶元素 \((1, 10)\)\((1,9)\)\(a\) 值相同,继续弹栈。此时栈空,\(4\) 号二元组入栈,栈为 \(\{(1, 9)\}\),该二元组是“成功的”。共有 \(3\) 个二元组是“成功的”,因而答案为 \(3\)

【样例 2,3,4】

见附件 \(\texttt{stack/stack*.in}\)\(\texttt{stack/stack*.ans}\)

链接:https://pan.baidu.com/s/14XxLN63bxvpJAod81foGOg 提取码:gugu

【数据范围与提示】

对于所有测试点:\(1 \leq n, q \leq 5 \times 10^5\)

每个测试点的具体限制见下表:

测试点编号 特殊性质\(1 \sim 3\) \(n,q \leq 1000\)\(4 \sim 6\) \(n \leq 5000\)\(7 \sim 10\) \(n,q \leq 10^5\)\(11 \sim 12\) \(b_i=n-i+1\)\(13 \sim 15\) \(a_i=i\)\(16 \sim 20\)

先按照题意模拟求出一个数在栈中的上一位是谁,设上一个数是\(a_i\),这个可以模拟出来。那么设现在的询问求\(l\)\(r\)的中有多少个"成功的"数,其实也就是再求\(a_l,a_{l+1}\cdots a_r\)中有多少个小于l。只要他的上一位是在l的前面,那么就代表他在弹栈的过程中整个栈他都可以退完,栈中只有他一个数,他就是"成功的"。
怎么求有多少个数小于l呢?这是主席树的模板。建立主席树,每一个位置i都建立新的版本,并插入\(a_i\)。那么我们可以知道在前r个数里面有多少个数小于\(l\),也可以在前面\(l-1\)个数里有多少个小于l,然后相减就是结果。当然,我们也可以缩小一下常数,前面\(l-1\)个数肯定怎么弹都是在\(l\)的前面,所以直接减去\(l-1\)即可。

#include<cstdio>
const int N=5e5+5;
int n,q,a[N],b[N],st[N],rt[N],idx,tp,l,r;
struct node{
	int s,lc,rc;
}tr[N*40];
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while (ch<'0'||ch>'9')
	{
		if(ch=='-')
			f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=x*10+ch-48;
		ch=getchar();
	}
	return x*f;
}
void insert(int o,int p,int l,int r,int x)
{
	if(l==r)
	{
		tr[o].s=tr[p].s+1;
		return;
	}
	int md=l+r>>1;
	if(md>=x)
	{
		tr[o].lc=++idx,tr[o].rc=tr[p].rc;
		insert(tr[o].lc,tr[p].lc,l,md,x);
	}
	else
	{
		tr[o].rc=++idx,tr[o].lc=tr[p].lc;
		insert(tr[o].rc,tr[p].rc,md+1,r,x);
	}
	tr[o].s=tr[tr[o].rc].s+tr[tr[o].lc].s;
}
int ask(int o,int l,int r,int x,int y)
{
	if(x<=l&&r<=y)
		return tr[o].s;
	int md=l+r>>1,ret=0;
	if(md>=x)
		ret+=ask(tr[o].lc,l,md,x,y);
	if(md<y)
		ret+=ask(tr[o].rc,md+1,r,x,y);
	return ret;
}
int main()
{
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++)
		a[i]=read();
	for(int i=1;i<=n;i++)
		b[i]=read();
	for(int i=1;i<=n;i++)
	{
		while(tp&&(a[st[tp]]==a[i]||b[st[tp]]<=b[i]))
			--tp;
		rt[i]=++idx;
		insert(rt[i],rt[i-1],0,n,st[tp]);
		st[++tp]=i;
	}
	while(q--)
	{
		l=read(),r=read();
		printf("%d\n",ask(rt[r],0,n,0,l-1)-l+1);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值