HDU 6406 Taotao Picks Apples(方法小结:线段树/单调栈/二分)

25 篇文章 0 订阅
21 篇文章 0 订阅

题目链接

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

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)));
        }
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值