HDU 6406 Taotao Picks Apples(前缀和+二分)

HDU 6406 Taotao Picks Apples(前缀和+二分)

Time Limit: 2000/2000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 1282 Accepted Submission(s): 396

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.

题意

  给一个序列,每次贪心选取比前一个更大的数。每次询问修改一个数,求修改后的序列的能选出多少个数。询问不叠加。

解题思路

  这个题标程里面给的是ST+二分,也能用线段树写,但我想不到线段树如何维护区间。比赛的时候拿到就想到前缀和+二分了,复杂度是O(nlogn),写了几次wa了几次,心态有点崩,后来找到了一种类型的样例,发现没多少时间了,草草的去跟队友怼第一题,所幸怼过了。这题到最后都没写出来。

  比赛的时候一直想不通,对于该拿的点,如果把它变得特别小,那后面的原本不能拿的可能就会变成可以拿的,那我最开始预处理的前缀数组就用不上了。比赛结束后看了一个大佬的博客http://www.bubuko.com/infodetail-2729068.html,他的思路跟我的一样,他在处理我说的那种情况的时候很巧妙,用了一个不定长数组,存下除去第i个元素,其后剩余元素与其前面的元素构成的一层“阶梯”的元素,细节看代码吧,那一部分借鉴了大佬的代码。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+50;

int arr[maxn],aaa[maxn],bbb[maxn],different[maxn];
//arr数组保存的是原序列
//aaa数组存的是max(arr[1,i])
//bbb数组存的是前缀
//different数组存的是原本该拿走的值
vector<int> vec[maxn];
//该数组里本质上是除去第i个元素,其后剩余元素与其前面的元素构成的一层“阶梯”

int main()
{
#ifdef DEBUG
    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);
#endif // DEBUG
    int t,tmp,cnt,next,flag,pos;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1; i<=n; i++) scanf("%d",&arr[i]);
        if(n==1) //处理n=1的情况
        {
            for(int i=0; i<m; i++) printf("1\n");
            continue;
        }
        for(int i=0; i<=n; i++) vec[i].clear();//清空

        tmp=-1,cnt=0,next=-1,flag=0;
        aaa[0]=0;
        for(int i=1; i<=n; i++)
        {
            if(arr[i]>tmp)
            {
                next=-1;
                tmp=arr[i];
                different[cnt++]=tmp;
                pos=i;
                flag=0;
            }
            if(!flag) flag=1;
            else
            {
                if(arr[i]>next&&arr[i]>different[cnt-2])//除去能摘的i个元素剩余的能摘的就入数组
                {//所以这里需要与different[cnt-2]比较,不会RE
                    next=arr[i];
                    vec[pos].push_back(next);
                }
            }
            aaa[i]=tmp;
            bbb[i]=cnt;
        }
        while(m--)
        {
            int u,v,sum=0;
            scanf("%d%d",&u,&v);
            if(v>arr[u])
            {
                if(v<=aaa[u-1]) sum=bbb[n];
                else
                {
                    sum+=1+bbb[u-1];//前半段不受影响
                    int id=upper_bound(different,different+cnt,v)-different;
                    if(id!=cnt) sum+=cnt-id;
                }
            }
            else if(v<arr[u])
            {
                sum+=bbb[u-1];
                if(v>aaa[u-1]) sum++;
                if(arr[u]==aaa[u])//对于原先该摘的点
                {
                    int id=upper_bound(vec[u].begin(),vec[u].end(),v)-vec[u].begin();//看对这一维的影响,找到位置
                    sum+=vec[u].size()-id;
                    int idd=upper_bound(different,different+cnt,arr[u])-different;
                    if(idd!=cnt) sum+=cnt-idd;
                }
                else sum=bbb[n];
            }
            else sum=bbb[n];
            printf("%d\n",sum);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值