hdu 6406 Taotao Picks Apples 【ST表预处理】 2018杭电多校第八场 Contest 8 1010

本文探讨了一种算法挑战,即在给定一系列苹果高度的情况下,如何通过特定规则摘取最多的苹果。通过预处理和使用ST表进行二分查找,实现了高效解答。文章详细介绍了算法思路、代码实现及复杂度分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目如下:
原题目:
这里写图片描述
= =因为不会长截图所以只能这样了。
题意:
给你一排苹果的高度,每次只能摘第一个或者比上一次摘的苹果更高的苹果,问最多摘几个。(应该是强制摘,即不贪心求最优)。每组样例给你n个苹果的高度和m次询问,每次询问修改一个苹果的高度,且无后效性(这次询问不对下次询问有影响,即每次都从原队列里修改。)
分析:
T=10,n,m=1e5。暴力时间是 Tmn=1e11.爆炸。
然后,我们能接受的时间复杂度是O(T(n+m)log(n))。即所有使用二分和一次遍历的算法是可以接受的。
想了很久,最后用了拼接的算法。具体和答案类似,直接上答案:

从对原序列进行预处理着手。
发现,原序列断开,我们可以通过左右段的某些信息来拼接答案。
对于左段来说:
知道左边一格的答案(当前最大值与苹果数量)(1次遍历)
对于右段来说:
找到第一个大于此前的数(到断点为止的最大值)->(二分ST表,找最靠左的第一个大于当前最大值的数)->求从这一点开始到序列尾部的答案。(预处理遍历 t=2*n,用二分可以变成n+logn (? _?),即倒着存储用upper_bound,又PS:这里错了好多,没对upper_bound理解正确,不过最坏情况不过是多历遍一次所以不会有问题。)
对于每次询问:
• 考虑左段加上这个数之后的答案和最大值;
• 再找到右边第一个大于左半部分最大值的数,答案相加即可。
预处理使用ST 表,每次查询需要一个二分,总复杂度O(4*n+nlogn+logn)=O(nlogn)。
即除了ST表初始化,其他时间都很短。
最后上代码。
代码

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

#define inf 0x3fffffff
#define ll long long
#define  db double
#define  s(x) scanf("%d",&x);
#define  sd(x) scanf("%lld",&x);
#define  sf(x) scanf("%lf",&x);
#define  rep(i,m,n) for(i=m;i<=n;i++)
#define  mem(a) memset(a,0,sizeof(a))
int sum[100010];//源数据

int num[100010];
int pmaxn[100010];
int pans[100010];
int sans[100010];
ll m,n;


int dp[100001][18];

void ST()
{
    for (int i = 1; i <= n; i++)
        dp[i][0] =sum[i];
    for (ll j = 1; (1 << j) <= n; j++)
    {
        for (ll i = 1; i + (1 << j) - 1 <= n; i++)
        {
            dp[i][j] = max(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
        }
    }
}
int findd(int l,int k,int vaule)
{
    if(k==0)
        return l;
    k--;
    if(dp[l][k]>vaule) return findd(l,k,vaule);
    return findd(l+(1<<k),k,vaule);
}
int RMQ(int l, int r,int vaule)
{
    //if(l==r)return sum[l];
    int k = 0,a,b;
    if(l>r) return -1;
    if(l==r)
    {
        if(sum[l]>vaule)return l;
        else return -1;
    }

    while ((1 << (k + 1)) <= r - l + 1) k++;
    //cout<<dp[l][k]<<endl;
    //cout<<dp[r - (1 << k) + 1][k]<<endl;
    a=dp[l][k];
    if(a>vaule) return findd(l,k,vaule);
    b=dp[r - (1 << k) + 1][k];
    if(b>vaule) return findd(r - (1 << k) + 1,k,vaule);
    else
        return -1;
}
void init()
{
    int k,a,i,j;
    pmaxn[1]=sum[1];
    pans[1]=1;
    a=1;
    for(i=2; i<=n; i++)//前缀答案
    {
        if(pmaxn[i-1]>=sum[i])
        {
            pmaxn[i]=pmaxn[i-1];
            pans[i]=a;
        }
        else
        {
            pmaxn[i]=sum[i];
            pans[i]=++a;
        }
    }
    a=1;
    num[0]=sum[n];
    sans[n]=1;
    for(i=n-1; i>0; i--) //尾缀答案
    {
        if(num[0]<=sum[i])
        {
            sans[i]=1;
            num[0]=sum[i];
            a=1;
        }
        else
        {
            for(j=a-1;j>=0;j--)
            if(num[j]>sum[i])break;
            j++;
            num[j]=sum[i];
            a=j+1;
            sans[i]=a;
            /*k=0;
            k=upper_bound(num,num+a,sum[i])-num;
            k+=1;
            num[k]=sum[i];
            k+=1;
            a=k;
            sans[i]=a;*/
        }
    }
    /*for(i=1;i<=n;i++)cout<<sans[i]<<" ";
        cout<<endl<<endl;*/
    //rmq
    ST();
}

void solve(int index)
{
    int k,a,b;
    if(index==1)
    {
        k=RMQ(2,n,sum[1]);
        if(k==-1) cout<<1<<endl;
            else //cout<<sum[k]<<" "<<k<<" ",
                cout<<1+sans[k]<<endl;
    }
    else if(index==n)
    {
        k=pans[n-1];
        if(sum[index]>pmaxn[n-1])
            k++;
        cout<<k<<endl;
    }
    else
    {
        a=pans[index-1];
        if(pmaxn[index-1]>=sum[index])
        {
            b=pmaxn[index-1];
        }
        else
        {
            b=sum[index];
            a++;
        }
        k=RMQ(index+1,n,b);
        if(k==-1)cout<<a<<endl;
        else cout<<a+sans[k]<<endl;
    }

}

int main()
{
#ifdef _zxdin_
    freopen("C:\\Users\\A\\Desktop\\data.in","r",stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t,i,j,k,a,b,c;
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        rep(i,1,n) cin>>sum[i];

        if(n==1)
        {
            while(m--)
            {
                cin>>a>>b;
                cout<<1<<endl;
            }
            continue;
        }

        init();

       /* for(i=1;i<=n;i++)cout<<sans[i]<<" ";
        cout<<endl<<endl;*/

        // cout<<"st: "<<n<<endl;

        rep(i,1,m)
        {
            //cout<<i<<": "<<endl;
            cin>>a>>b;
            c=sum[a];
            sum[a]=b;
            //cout<<"ss: "<<n<<endl;
            solve(a);
            sum[a]=c;
            //cout<<"end: "<<n<<endl;
        }
    }
    return 0;
}

末尾语:
这是我的第一篇博文,代码里有很多事debug的段落被注释掉了但我不会删。写博客是因为回顾与记录,希望能从这道题里学到算法和错漏。以上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值