HDU 6406 Taotao Picks Apples 线段树,模拟,单调栈

题意:长度为n的序列a,每次从第一个数开始,拿一个严格大于上次拿的数.
m次询问:问将a[p]=x时,总共能拿多少个数?   n,m<=1e5, 1<=a[i]<=1e9.

当a[p]变为x时, 要先找到[1:p-1]中选的最后一个y.
此时只要找到[p+1,n]中第一个比max(x,y)大的下标q.线段树维护最值来即可.

预处理出d1[i]表示前缀i选的个数. 以及d2[i]:从a[i]开始选时的个数.
求d1顺着扫一遍, 求d2单调栈预处理后,逆着扫一遍即可.
那么答案就是:d1[p-1] + d2[q] +(y>x?)

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int T,n,m,a[N],R[N],pos[N],cur,d1[N],d2[N];
stack<int> st;
struct node{
	int l,r,mx,id;
}t[N<<2];
void push_up(int o){
	int ls=o<<1,rs=o<<1|1;
	if(t[ls].mx>=t[rs].mx)	t[o].mx=t[ls].mx,t[o].id=t[ls].id;
	else	t[o].mx=t[rs].mx,t[o].id=t[rs].id;
}
void build(int o,int l,int r){
	t[o].l=l,t[o].r=r;
	if(l==r){
		t[o].mx=a[l];
		t[o].id=l;
		return;
	}
	int mid=l+r>>1;
	build(o<<1,l,mid);
	build(o<<1|1,mid+1,r);
	push_up(o); 
}
// query [l,r]中第一个大于k的下标. 
void query(int o,int ql,int qr,int k){
	int l=t[o].l,r=t[o].r; 
	if(l==r)
	{
		if(t[o].mx>k)	cur=min(cur,l);
		return;
	}
	if(l>=ql&&r<=qr){
		if(t[o].mx<=k)	return;
		if(t[o<<1].mx>k)	query(o<<1,ql,qr,k);
		else	query(o<<1|1,ql,qr,k);
		return;
	}
	int mid=l+r>>1;
	if(ql<=mid)	query(o<<1,ql,qr,k);
	if(qr>mid) query(o<<1|1,ql,qr,k);
} 
void solve(){
	int p,x;
	while(m--){
		scanf("%d%d",&p,&x);
		int y=a[pos[p-1]],k=max(x,y);
		cur=n+1;
		query(1,p+1,n,k);
		int res=d1[p-1]+d2[cur];
		if(x>y)	res++;
	//	cout<<p<<' '<<x<<' '<<y<<' '<<cur<<' '<<d2[cur]<<' '<<'\n';
		printf("%d\n",res);
	}
}
int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&m);
		d1[0]=d2[n+1]=0;
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			while(!st.empty()&&a[st.top()]<a[i])
				R[st.top()]=i,st.pop();
			st.push(i);
		}
		while(!st.empty())	R[st.top()]=n+1,st.pop();
		
		int pre=0,val=0;
		for(int i=1;i<=n;i++){
			d1[i]=d1[i-1],pos[i]=pos[i-1];
			if(a[i]>pre)	d1[i]++,pre=a[i],pos[i]=i;
		}
		for(int i=n;i>=1;i--)	d2[i]=1+d2[R[i]];
		build(1,1,n);
		solve();
	}
	return 0;
} 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值