P7818排列
FreeTimeLove · · 题解
这是我的第一篇题解。感谢@vectorwyx大佬不吝赐教。
题意为求将一个给定的全排列交换相邻的数
既然要求字典序最小,那么一定要靠前的数尽量小,我们就想到了贪心算法:
设当前操作到了第
具体地说,我们用树状数组维护原序列第
每次操作,如果所有未选过的数都能交换到第
最多进行
但是这样交上去,我们却发现 WA 了
显然如果次数多余,当前的全排列一定已经是递增的。如果剩余偶数次,交换最后两位
AC code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#define ll long long
using namespace std;
const int N=4e6+5,INF=0x3f3f3f3f;
ll n,k,a[N],bk[N],pos,tot,ans[N];
struct xxs{
ll p,num;
}x;
namespace BIT{
ll c[N];
void add(int x,ll k){
while(x<=n){
c[x]+=k;
x+=x&-x;
}
}
ll search(int x){
ll ans=0;
while(x){
ans+=c[x];
x-=x&-x;
}
return ans;
}
}
int half(int lim){
int mid,l=1,r=n+1;
while(l<r){
mid=(l+r)>>1;
if(BIT::search(mid)>=lim)
r=mid;
else
l=mid+1;
}
return l;
}
namespace SGT{
xxs c[N];
int L[N],R[N];
void pu(int rt){
if(c[rt<<1].num<c[rt<<1|1].num)
c[rt]=c[rt<<1];
else
c[rt]=c[rt<<1|1];
}
void build(int l,int r,int rt){
L[rt]=l,R[rt]=r;
if(l==r){
c[rt]=(xxs){l,a[l]};
return ;
}
int mid=(l+r)>>1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
pu(rt);
}
void upd(int x,ll k,int rt){
if(L[rt]>x||R[rt]<x)
return;
if(L[rt]==R[rt]){
c[rt].num=k;
return;
}
upd(x,k,rt<<1);
upd(x,k,rt<<1|1);
pu(rt);
}
xxs qry(int l,int r,int rt){
if(L[rt]>r||R[rt]<l)
return (xxs){1,INF};
if(L[rt]>=l&&R[rt]<=r)
return c[rt];
xxs lx=qry(l,r,rt<<1);
xxs rx=qry(l,r,rt<<1|1);
return (lx.num<rx.num?lx:rx);
}
}
int main(){
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
bk[i]=1;
BIT::add(i,1);
}
SGT::build(1,n,1);//维护区间最小值及其初始位置
for(int xx=1;xx<=n&&k;xx++){
if(k<n-xx)
pos=half(k+1);
else
pos=n;
x=SGT::qry(1,pos,1);
k-=BIT::search(x.p)-1;//交换次数=当前为序列中未选的第几个数-1
ans[++tot]=x.num;
//标记为已选
bk[x.p]=0;
BIT::add(x.p,-1);
SGT::upd(x.p,INF,1);
}
if(k&& k&1)swap(ans[n-1],ans[n]);
if(tot<n)//未被选的依次排到队尾
for(int i=1;i<=n;i++)
if(bk[i])
ans[++tot]=a[i];
for(int i=1;i<=n;i++)
cout<<ans[i]<<' ';
cout<<endl;
return 0;
}