Taotao Picks Apples
Time Limit: 2000/2000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 860 Accepted Submission(s): 251
Problem Description
There is an apple tree in front of Taotao's house. When autumn comes, n apples on the tree ripen, and Taotao will go to pick these apples.
When Taotao picks apples, Taotao scans these apples from the first one to the last one. If the current apple is the first apple, or it is strictly higher than the previously picked one, then Taotao will pick this apple; otherwise, he will not pick.
Given the heights of these apples h1,h2,⋯,hn, you are required to answer some independent queries. Each query is two integers p,q, which asks the number of apples Taotao would pick, if the height of the p-th apple were q (instead of hp). Can you answer all these queries?
Input
The first line of input is a single line of integer T (1≤T≤10), the number of test cases.
Each test case begins with a line of two integers n,m (1≤n,m≤105), denoting the number of apples and the number of queries. It is then followed by a single line of n integers h1,h2,⋯,hn (1≤hi≤109), denoting the heights of the apples. The next m lines give the queries. Each of these m lines contains two integers p (1≤p≤n) and q (1≤q≤109), as described in the problem statement.
Output
For each query, display the answer in a single line.
Sample Input
1
5 3
1 2 3 4 4
1 5
5 5
2 3
Sample Output
1 5 3
Hint
For the first query, the heights of the apples were 5, 2, 3, 4, 4, so Taotao would only pick the first apple. For the second query, the heights of the apples were 1, 2, 3, 4, 5, so Taotao would pick all these five apples. For the third query, the heights of the apples were 1, 3, 3, 4, 4, so Taotao would pick the first, the second and the fourth apples.
题意:
给你一个序列,m次询问,每一次c,b将a[c]=b,然后求[1-n]最大值变换的次数,并输出,每一个变化的更新不会保存
解析:
这道题目大致看了一下,不同的数据结构用的很多,不过大致思想是一样的
就是将求出[1,c-1]的最大值及bmax其最大值变换次数bans,然后取bmax和b的最大值smax,在[c+1,n]求这个最大值之后的最大值变换次数
1.线段树,就是用线段树维护区间最大值及其下标,前面那一部分区间挺好求的,后面那部分区间我看了代码,说实话挺暴力的。求第一个大于smax的位置,我看是通过只有当ql<=l&&r<qr时,再根据区间最大值,选择往左还是往右搜,否则的话都是跟普通的搜索一样,按照区间搜。链接
2.离线单调栈,这个就是把询问都离线出来,根据pos从小到大排序,将每一次询问对应前一部分区间的值(smax,bans)保存在ans数组中(与询问的id对应),再根据询问的pos从后往前扫,每一次一个pos,都维护从当前[pos,n]的单调栈,这样再二分当前单调栈,寻找第一个大于smax的值,这样就可以求出答案了链接
3.直接二分,这种就是我们比赛的时候a出来的思路,就是考虑各种情况
譬如一组 5 10 8 9 100 2 3 23 41 30 75 88 102 103 33
答案队列fir就是5 10 100 102 103
id 1 2 5 13 14
询问是8 101时,我们发现id为8的元素不是fir的元素,他属于[5,13]这个区间内
所以我们首先比较他跟100哪个大,然后再在fir内二分找第一个大于他的元素
所以只要不是fir内的元素,都用上述方法处理
一旦是fir内的元素,那么我们就需要判断它与原来的数的大小
它比原来的数大的话,那么还是按上述处理,在fir内二分找第一个大于他的元素
对于比原来数小的情况 5 1,那么首先需要比较他与fir前面哪个元素的大小,取最大值,这里就变成10
然后在[6,12]的区间内找第一个比10大的元素,再把原来的答案+[6,12]这个区间的答案就可以了
但是这里还需要-1,因为我们在fir中id为5的这个元素就消失了,因为他变得比10小了
st表其实也可以用来求区间最大值,题解里面st表也就是用来求区间最大值用的。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int MAXN = 1e5+10;
typedef struct node
{
int x;
int id;
}node;
node fir[MAXN];
int cnt1;
vector<node> bf[MAXN];
int a[MAXN];
bool cmp1(node a,node b)
{
return a.id<b.id;
}
bool cmp2(node a,node b)
{
return a.x<b.x;
}
int solve(int c,int val)
{
int past;
int ans=0;
node tmp=node{val,c};
int ind1=lower_bound(fir+1,fir+1+cnt1,tmp,cmp1)-fir; //寻找当前改变的id对应fir对应的区间
if(ind1==1+cnt1)
{
if(fir[cnt1].x<val) return cnt1+1;
else return cnt1;
}
else if(fir[ind1].id==c) //如果改变的值正好是fir里面的元素
{
if(val>=a[c]) //如果原来的值小于改变的值,那么只需要在fir中[ind1,cnt1]的区间第一个大于val的值就可以了
{
int ano=upper_bound(fir+ind1,fir+1+cnt1,tmp,cmp2)-fir;
ans=ind1+cnt1-ano+1;
return ans;
}
else //如果原来的值大于改变的值
{
if(fir[ind1-1].x>=val) ans--,tmp.x=fir[ind1-1].x; //如果fir前面的元素大于改变的值,则当前的最大值应该是fir[ind1-1]
int num=bf[ind1].size();
int inb=upper_bound(bf[ind1].begin(),bf[ind1].end(),tmp,cmp2)-bf[ind1].begin();//在[fir[ind1].id,fir[ind1].id)这段区间找第一个大于当前最大值的元素
ans=ans+cnt1+num-inb;
return ans;
}
}
else //如果修改的元素不是fir内的元素,则只需要在fir中[ind1,cnt1]的区间第一个大于val的值就可以了
{
past=a[c];
if(ind1-1>=1&&fir[ind1-1].x<val) ans++; //修改的值增大,使得其成为fir[ind1].id后面第一个最大值
else if(ind1-1>=1&&fir[ind1-1].x>=val||fir[ind1].x>=val) //修改的最大值对原来答案队列无影响
{
return cnt1;
}
int ano=upper_bound(fir+ind1,fir+1+cnt1,tmp,cmp2)-fir;
ans=ans+ind1-1+cnt1-ano+1;
return ans;
}
return ans;
}
int main()
{
int t;
int n,m;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
cnt1=0;
fir[++cnt1]={a[1],1};
for(int i=2;i<=n;i++) //处理出初始最大值变化的数组,即答案队列
{
if(a[i]>fir[cnt1].x) fir[++cnt1]=node{a[i],i};
}
for(int i=1;i<=cnt1;i++) //再处理出在fir相邻的两个数对应的那一段区间[fir[i].id,fir[i+1].id)的最大值变化的数组
{
bf[i].clear();
int cb=fir[i].id+1;
int ce;
if(i==cnt1)
{
ce=n+1;
}
else
{
ce=fir[i+1].id;
}
int cw=0;
for(int j=cb;j<ce;j++)
{
if(a[j]>cw)
{
bf[i].push_back(node{a[j],j});
cw=a[j];
}
}
}
int c,y;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&c,&y);
printf("%d\n",(solve(c,y)));
}
}
}