• 第十一届蓝桥杯模拟赛10 数节目(ST表)


    原题
    题意:求长度为k且字典序最大的子序列

    用了ST表做,相比单调栈多了点代码,但也不是很长。
    我们可以用ST表来存区间最值的下标,比如说我们某次要取一个数,就要从上次查询出的最值的后面选,同时为了保证后面的数不会不够选(如果还有k个数没选),我们要保证剩下的区间至少还剩 k 的长度,假设上次选出的值位置在 c ,还剩 k 个数没选,那我们接下来可以从[c+1,n-k+1]的区间范围内开始寻找。
    因为一个小细节没处理好,搞了一下午,还用了对拍,气死我了!感觉这题范围再大点,我就要MLE了...

    #include<bits/stdc++.h>
    using namespace std;
    int f[1000005][22],x[1000005],ans[1000005];
    int n,k;
    void ST_work()
    {
        for(int i=1; i<=n; i++)
            f[i][0]=i;//先初始化为本身
        for(int j=1; (1<<j)<=n; j++)
            for(int i=1; i+(1<<j)-1<=n; i++)
            {
                int a=f[i][j-1],b=f[i+(1<<(j-1))][j-1];
                if(x[a]>=x[b])//这里一定要注意,一定是大于等于,因为如果两个数相等,我们要选前面一个,这样保证后面那个一样大小的数在需要时也能被选到
                    f[i][j]=a;
                else
                {
                    f[i][j]=b;
                }
            }
    }
    int ask(int l,int r)
    {
        int n=log(r-l+1)/log(2);
        int a=f[l][n],b=f[r-(1<<n)+1][n];
        if(x[a]>=x[b])//同上
            return a;
        else
            return b;
    }
    int main()
    {
        while(cin>>n>>k)
        {
            memset(f,0,sizeof(f));
    
            for(int i=1; i<=n; i++)
                scanf("%d",&x[i]);
            ST_work();
            int s=1,e=n-k+1,cot=0,c;
            while(k--)
            {
                c=ask(s,e);
                ans[cot++]=c;
                s=c+1;//从后面找
                e++;//尾指针也后移,类似滑动窗口
            }
            for(int i=0; i<cot; i++)
            {
                cout<<x[ans[i]];
                if(i!=cot-1)
                    cout<<' ';
            }
            puts("");
        }
        return 0;
    }
    
    
    戒骄戒躁,百炼成钢!
  • 相关阅读:
    转职游戏策划第四周
    转职游戏策划第三周
    HTML表单验证库SMValidator
    win7通过easyBCD引导ubuntu
    SWF混淆工具(智能提取需要混淆的字段)
    图文混排组件(RichTextField)
    windows下gvim使用vundle插件
    AirMVC
    再战中原之地图编辑器
    再战中原之菜单系统
  • 原文地址:https://www.cnblogs.com/Pecoz/p/12519899.html
Copyright © 2020-2023  润新知