【51NOD1364】最大字典序排列

题面

给出一个1至N的排列,允许你做不超过K次操作,每次操作可以将相邻的两个数交换,问能够得到的字典序最大的排列是什么?
例如:N = 5, {1 2 3 4 5},k = 6,在6次交换后,能够得到的字典序最大的排列为{5 3 1 2 4}。1 <= N <= 100000, 0 <= K <= 10^9

分析

贪心,找第i个位置的数的时候,寻找在i+k的范围内的最大的数,然后将k更新为这个数的位置减去i,然后把这个数改为一个负数,使它无法再被找到。

关键在于如何保存哪些数已经被选过的信息。其实和区间交类似,区间交那题维护了在一个范围内左端点的个数从而能返回左端点位置第k小。

我们也可以维护未被选的元素个数,这样每次都可以查找到元素个数为k+1的区间[1,R]的右端点R。再在[1,R]里查找最大元素,最后删除这个元素。

MD少打两return查一下午 而且这玩意儿居然没给我炸掉??爆哭

代码

#include<bits/stdc++.h>
using namespace std;
#define N 100100
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (t[p].l+t[p].r>>1)
int n,k,mx,now,pos,cnt=1;
int a[N];
struct email
{
    int l,r,sum,maxx,pos;
}t[N*4];
inline void pushup(int p)
{
    if(t[lc].maxx>=t[rc].maxx)t[p].maxx=t[lc].maxx,t[p].pos=t[lc].pos;
    else t[p].maxx=t[rc].maxx,t[p].pos=t[rc].pos;
    t[p].sum=t[lc].sum+t[rc].sum;
}

inline void build(int p,int l,int r)
{
    t[p].l=l;t[p].r=r;
    if(l==r)
    {
        t[p].maxx=a[l];t[p].sum=1;t[p].pos=l;
        return ;
    }
    int bm=l+r>>1;
    build(lc,l,bm);build(rc,bm+1,r);
    pushup(p);
}

inline void update(int p,int x)
{
    if(t[p].l==t[p].r)
    {
        t[p].maxx=t[p].sum=0;
        return ;
    }
    if(x<=mid)update(lc,x);
    if(x>mid)update(rc,x);
    pushup(p);
}

inline void query(int p,int ql,int qr)
{
    if(ql<=t[p].l&&qr>=t[p].r)
    {
        if(mx<t[p].maxx)pos=t[p].pos,mx=t[p].maxx;
        return ;
    }
    if(ql<=mid)query(lc,ql,qr);
    if(qr>mid)query(rc,ql,qr);
    pushup(p);
}

inline int find(int p,int x)
{
    if(t[p].l==t[p].r)return t[p].l;
    if(x<=t[lc].sum)return find(lc,x);
    if(x>t[lc].sum)return find(rc,x-t[lc].sum);
}

inline int query_sum(int p,int ql,int qr)
{
    int ret=0;
    if(ql<=t[p].l&&qr>=t[p].r)return t[p].sum;
    if(ql<=mid)ret+=query_sum(lc,ql,qr);
    if(qr>mid)ret+=query_sum(rc,ql,qr);
    return ret;
}

int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
     build(1,1,n);
     while(k>0&&cnt<=n)
     {
         mx=0;pos=n;
         if(cnt+k>=n)query(1,1,n);
         else now=find(1,k+1),query(1,1,now);
         update(1,pos);k-=query_sum(1,1,pos);
         a[pos]=-1;cnt++;
         printf("%d\n",mx);
     }
    for(int i=1;i<=n;i++)
        if(a[i]!=-1)
             printf("%d\n",a[i]);
     return 0;
}

 

转载于:https://www.cnblogs.com/NSD-email0820/p/9805389.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值