无法拯救我的菜系列------hdu6406

补题持续中……….
参考:线段树做法:https://blog.csdn.net/ccsu_cat/article/details/81711440
莫队做法+单调栈:https://blog.csdn.net/qq_34454069/article/details/81734845

Taotao Picks Apples
Time Limit: 2000/2000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 1848 Accepted Submission(s): 571

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.

这道题的做法很多,就是维护询问点的左段和右段的最长严格递增子序列
第一种做法:(感觉很妙)
线段树:利用线段树维护区间最值,再维护一个同样的下标线段树,预处理两个数组,L[]代表从起点到a[i]的最长严格递增子序列长度,R[]代表a[i]到末尾的最长严格递增子序列的长度,处理R时,可用线段树(log(n)的复杂度)找到i点右边第一个大于a[i]的数的下标,那么R[i] = R[cur] + 1;如果没有找到,R[i] = 1;
处理R数组也可以用单调栈或单调队列,是O(n)的复杂度
赛场上处理R数组一直是O(n ^ 2)复杂度,还是见的少啊。。。。
1、设置一个最高高度变量代表询问点左区间的最大值,假设该最高高度是第i棵树,结果加上L[i];
2、如果询问点的高度大于左区间的最大高度,更新最高高度,将最高高度设为当前值,结果+1;
3、再用线段树找到右区间第一个大于最高高度的下标j,结果加上R[j]

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f;
const int N = 1e5 + 5;
int L[N],R[N],a[N];
int tree[N << 2],vis[N << 2];
int cur;

void pushdown(int root,int l,int r)
{
    tree[root] = max(tree[l],tree[r]);
    if(tree[l] > tree[r]){
        vis[root] = vis[l];
    }
    else{
        vis[root] = vis[r];
    }
}

void build(int root,int l,int r)
{
    if(l == r){
        tree[root] = a[l];
        vis[root] = l;
        return ;
    }
    int mid = (l + r) / 2;
    build(root * 2 + 1,l,mid);
    build(root * 2 + 2,mid + 1,r);
    pushdown(root,root * 2 + 1,root * 2 + 2);
}
//查找区间第一个大于k的数
void query(int root,int l,int r,int pl,int pr,int k)
{
    if(l == r){
        if(tree[root] > k){
            cur = min(cur,vis[root]);
        }
        return ;
    }
    int ls = 2 * root + 1,lr = 2 * root + 2;
    int mid = (l + r) / 2;
    if(pl <= l && pr >= r){
        if(tree[ls] > k){
            query(ls,l,mid,pl,pr,k);
        }else if(tree[lr] > k){
            query(lr,mid + 1,r,pl,pr,k);
        }
        return;
    }
    if(pl <= mid){
        query(ls,l,mid,pl,pr,k);
    }
    if(pr > mid){
        query(lr,mid + 1,r,pl,pr,k);
    }
}
//查找区间最大值的下标
void query1(int root,int l,int r,int pl,int pr)
{
    if(pl <= l && pr >= r){
        if(tree[root] > a[cur]){
            cur = vis[root];
        }
        return ;
    }
    int ls = 2 * root + 1,lr = 2 * root + 2;
    int mid = (l + r) / 2;
    if(pl <= mid){
        query1(ls,l,mid,pl,pr);
    }
    if(pr > mid){
        query1(lr,mid + 1,r,pl,pr);
    }
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        memset(L,0,sizeof(L));
        memset(R,0,sizeof(R));
        scanf("%d %d",&n,&m);
        int MAX = -1;
        L[0]= 0;
        for(int i = 1;i <= n;++i)
        {
            scanf("%d",&a[i]);
            if(a[i] > MAX){
                L[i] = L[i - 1] + 1;
                MAX = a[i];
            }
            else{
                L[i] = L[i - 1];
            }
        }
        build(1,1,n);
        R[n] = 1;
        for(int i = n - 1;i >= 1;--i)
        {
            //初始化下标为大于n的数,cur = inf,如果没找到大于a[i]的数,cur还为inf
            cur = inf;
            query(1,1,n,i,n,a[i]);
            if(cur == inf){
                R[i] = 1;
            }
            else{
                R[i] = R[cur] + 1;
            }
        }
        int ans = 0;
        while(m--)
        {
            int p,q;
            ans = 0;
            scanf("%d %d",&p,&q);
            cur = 0;
            if(p != 1){
                query1(1,1,n,1,p - 1);
            }
            ans += L[cur];
            //printf("cur:%d L:%d\n",cur,L[cur]);
            if(q > a[cur]){
                ans++;
            }
            else{
                q = a[cur];
            }
            cur = inf;
            if(p != n)
                query(1,1,n,p + 1,n,q);
            if(cur != inf){
                ans += R[cur];
                //printf("cur:%d R:%d\n",cur,R[cur]);
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}

第二种做法:
莫队算法+单调栈
将询问统计下来,进行离线处理,从左到右处理每个询问点之前的最长严格递增子序列的长度,再考虑当前询问点,是否可以在长度上加一,是否更新左区间(包括询问点)的最高高度,再从右往左处理右区间的最长严格递增子序列的长度(利用单调栈 + 二分);

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f;
const int N = 1e5 + 5;
typedef struct Node{
    int pos,val,id;
    friend bool operator < (const Node &p,const Node &q)
    {
        if(p.pos == q.pos){
            return p.val < q.val;
        }
        return p.pos < q.pos;
    }
}Node;
Node node[N];//离线询问点
int ans[N];//存结果
int a[N];//存树高
int sta[N];//单调栈
int dp[N];//右区间的最长严格递增子序列的长度

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        scanf("%d %d",&n,&m);
        for(int i = 1;i <= n;++i)
        {
            scanf("%d",&a[i]);
        }
        for(int i = 0;i < m;++i)
        {
            int p,q;
            scanf("%d %d",&p,&q);
            node[i].pos = p;
            node[i].val = q;
            node[i].id = i;
        }
        sort(node,node + m);
        int last = 0;
        int MAX = 0;
        int sum = 0;
        a[0] = -1;
        for(int i = 0;i < m;++i)
        {
            while(last < node[i].pos){
                if(a[last] > MAX){
                    MAX = a[last];
                    sum++;
                }
                last++;
            }
            //cout << i << " " << MAX << " " << sum << endl;
            if(node[i].val > MAX){
                ans[node[i].id] = sum + 1;
            }
            else{
                ans[node[i].id] = sum;
                //更新该点之前包括该点的最高高度
                node[i].val = MAX;
            }
        }
        last = n;
        int top = 0;
        for(int i = m - 1;i >= 0;--i)
        {
            //cout << 0 << endl;
            while(last > node[i].pos){
                while(top != 0 && a[sta[top]] <= a[last]){
                    top--;
                }
                if(top == 0){
                    dp[last] = 1;
                }
                else{
                    dp[last] = dp[sta[top]] + 1;
                }
                sta[++top] = last;
                last--;
            }
            int l = 1,r = top,res = 0;
            //cout << 1 << endl;
            while(l <= r)
            {
                int mid = (l + r) / 2;
                //cout << l << " " << mid << " " << r << endl;
                if(a[sta[mid]] > node[i].val){
                    res = sta[mid];
                    l = mid + 1;
                }
                else{
                    r = mid - 1;
                }
            }
            //cout << 2 << endl;
            ans[node[i].id] += dp[res];
        }
        for(int i = 0;i < m;++i)
        {
            printf("%d\n",ans[i]);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36386435/article/details/81782888