51nod 1364 线段树

来源:互联网 发布:猫小萱淘宝 编辑:程序博客网 时间:2024/06/09 16:56
给出一个1至N的排列,允许你做不超过K次操作,每次操作可以将相邻的两个数交换,问能够得到的字典序最大的排列是什么?
例如:N = 5, {1 2 3 4 5},k = 6,在6次交换后,能够得到的字典序最大的排列为{5 3 1 2 4}。


解题:

《System Messag 的题解》

这个题目明显是要贪心的。从前往后每次看看能不能拿一个大的放到当前数字的前面,而且要拿尽可能大的。这样字典序才会变成最大。

原数组设为a[i]

这个过程可以用线段树来维护。先按照原数组建立线段树。树结点中记录最大值,最大值所在位置,当前区间有几个数字没有被删除。

然后从前往后开始,设当前位置为P,剩K步可以拿,那么就要从P后面拿一个a[Q],他要比a[P]大且区间[P,Q]未删除数字个数不超过K+1。可以先找到距离P最远X的且[P,X]里面未删除的数字不超过K+1个。然后查询[P,X]中最大值,如果比a[P]大就可以换了,不然的话就P++Q放到前面之后就在线段树里面进行删除。

距离P最远X的且[P,X]里面未删除的数字不超过K+1个,这一步可以通过找[0,X]不超过某个数字来做,可以有logn.

以上步骤均可以在logn以内实现,所以总复杂度是nlogn


#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<string.h>#include<algorithm>using namespace std;#define ls rt<<1#define rs rt<<1|1const int maxn=200000;//删除//找区间最大值//可以分成两步写呀//线段树的话果然还是想好操作然后再想维护什么比较容易有思路struct node{    int l,r;    int ma;    int num;}T[maxn<<2];int times;int here[maxn];int flag[maxn];int a[maxn],a2[maxn];void pushup(int rt){    T[rt].num=T[ls].num+T[rs].num;    T[rt].ma=max(T[ls].ma,T[rs].ma);}void build(int rt,int L,int R){    T[rt].l=L;T[rt].r=R;    if(L==R)    {        T[rt].ma=a[L];        T[rt].num=1;        return;    }    int mid=(L+R)>>1;    build(ls,L,mid);    build(rs,mid+1,R);    pushup(rt);}//查询最大值并删除void update(int rt,int pos)//标记为0{//    cout<<T[rt].l<<" "<<T[rt].r<<" "<<T[rt].ma<<endl;    if(T[rt].l==T[rt].r)    {        T[rt].num=0;        T[rt].ma=0;        flag[T[rt].l]=1;        return;    }    int mid=(T[rt].l+T[rt].r)>>1;    if(pos<=mid) update(ls,pos);    else update(rs,pos);    pushup(rt);}int query(int rt,int L,int R){//    cout<<rt<<" "<<L<<" "<<R<<endl;    if(T[rt].l>=L && T[rt].r<=R)    {        return T[rt].ma;    }    int mid=(T[rt].l+T[rt].r)>>1;    int tmp=0;    if(L<=mid) tmp=max(tmp,query(ls,L,R));    if(R>mid) tmp=max(tmp,query(rs,L,R));//多个地方的 L R的处理    return tmp;}int getpos(int rt,int pos){//    cout<<T[rt].l<<" "<<T[rt].r<<" "<<T[rt].num<<endl;    if(T[rt].l==T[rt].r)    {        return T[rt].l;    }     if(T[ls].num>=pos) return getpos(ls,pos);     else return getpos(rs,pos-T[ls].num);}int gettimes(int rt,int L,int R){//    cout<<L<<" "<<R<<" "<<T[rt].num<<endl;    if(T[rt].l>=L && T[rt].r<=R)    {        return T[rt].num;    }    int mid=(T[rt].l+T[rt].r)>>1;    int tmp=0;    if(L<=mid)        tmp+=gettimes(ls,L,R);    if(R>mid)        tmp+=gettimes(rs,L,R);    return tmp;}int main(){    int n,k;//    freopen("in.txt","r",stdin);    while(scanf("%d%d",&n,&k)!=EOF)    {       for(int i=1;i<=n;i++)        scanf("%d",&a[i]),       here[a[i]]=i;       memset(flag,0,sizeof(flag));       build(1,1,n);       int p=0;       for(int i=1;i<=n;i++)       {           if(k==0) break;           p++;           int pos=getpos(1,k+1);           int tmp=query(1,1,pos);//           cout<<"pos "<< pos<<" ma"<<tmp<<endl;           update(1,here[tmp]);           k-=gettimes(1,1,here[tmp]);           a2[p]=tmp;//           cout<<k<<" k"<<endl;       }//       cout<<k<<endl;       for(int i=1;i<=n;i++)       {           if(flag[i]==0)           a2[++p]=a[i];       }//       cout<<"--------------------------------"<<endl;       for(int i=1;i<=n;i++)        cout<<a2[i]<<endl;    }    return 0;}


0 0