[CSP-S模拟测试]:陶陶摘苹果(线段树维护单调栈)

题目传送门(内部题116)


输入格式

  第一行两个整数$n,m$,如题
  第二行有$n$个整数表示$h_1-h_n(1\leqslant h_i\leqslant 10^9)$
  接下来有$m$行,每行两个整数,$p,H_p$,表示第$p$个位置的苹果实际高度为$H_p$。


输出格式

  输出共$m$行,每行一个整数,表示这种修改情况下的答案。


样例

样例输入:

5 3
1 2 3 4 4
1 5
5 5
2 3

样例输出:

1
5
3


数据范围与提示

样例解释:

  第一种情况 苹果高度为$5\ 2\ 3\ 4\ 4$,只摘第一个苹果
  第二种情况 苹果高度为$1\ 2\ 3\ 4\ 5$,陶陶会摘五个苹果
  第三种情况 苹果高度为$1\ 3\ 3\ 4\ 4$,所以会选择$1,2,4$号位置的苹果

数据范围:

  对于$20\%$的数据,$n,m\leqslant 5,000$
  对于$100\%$的数据,$n,m\leqslant 100,000$。


题解

做法很多,我的做法是线段树维护单调栈。

开场$20$分钟切,就是个板子,没什么好说的了……

时间复杂度:$\Theta(n\log^2n)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
#define L(x) x<<1
#define R(x) x<<1|1
using namespace std;
int n,m;
int a[200001];
int trmx[500001],trsm[500001];
int pushup(int x,int l,int r,int w)
{
	if(l==r)return trmx[x]>w;int mid=(l+r)>>1;
	if(trmx[L(x)]<=w)return pushup(R(x),mid+1,r,w);
	return trsm[x]-trsm[L(x)]+pushup(L(x),l,mid,w);
}
void pushup(int x,int l,int r)
{
	if(trmx[L(x)]<=trmx[R(x)])trmx[x]=trmx[R(x)];
	else trmx[x]=trmx[L(x)];int mid=(l+r)>>1;
	trsm[x]=trsm[L(x)]+pushup(R(x),mid+1,r,trmx[L(x)]);
}
void add(int x,int l,int r,int k,int w)
{
	if(l==r){trmx[x]=w;trsm[x]=1;return;}
	int mid=(l+r)>>1;
	if(k<=mid)add(L(x),l,mid,k,w);
	else add(R(x),mid+1,r,k,w);
	pushup(x,l,r);
}
pair<int,int> ask(int x,int l,int r,int k,int w)
{
	if(l>n)return make_pair(0,0);
	if(k<=l)return make_pair(max(w,trmx[x]),pushup(x,l,r,w));
	int mid=(l+r)>>1;
	if(mid<k)return ask(R(x),mid+1,r,k,w);
	pair<int,int> flag=ask(L(x),l,mid,k,w);
	pair<int,int> res=ask(R(x),mid+1,r,k,max(w,flag.first));
	res.second+=flag.second;
	return res;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		add(1,1,n,i,a[i]);
	}
	for(int i=1;i<=m;i++)
	{
		int p,h;
		scanf("%d%d",&p,&h);
		add(1,1,n,p,h);
		printf("%d\n",ask(1,1,n,1,0).second);
		add(1,1,n,p,a[p]);
	}
	return 0;
}

rp++

posted @ 2019-11-04 09:25  HEOI-动动  阅读(185)  评论(0编辑  收藏  举报