51Node 1364--- 最大字典序排列(树状数组)
基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题
收藏
关注
给出一个1至N的排列,允许你做不超过K次操作,每次操作可以将相邻的两个数交换,问能够得到的字典序最大的排列是什么?
例如:N = 5, {1 2 3 4 5},k = 6,在6次交换后,能够得到的字典序最大的排列为{5 3 1 2 4}。
Input
第1行:2个数N, K中间用空格分隔(1 <= N <= 100000, 0 <= K <= 10^9)。 第2至N + 1行:每行一个数i(1 <= i <= N)。
Output
输出共N行,每行1个数,对应字典序最大的排列的元素。
Input示例
5 6 1 2 3 4 5
Output示例
5 3 1 2 4
思路:先用a[]数组记录下输入的1~N的一个排列,并用p[]数组记录输入的排列中每个数的位置即:a[i]=x,p[x]==i;用树状数组c[]记录第i个位置的数距离前面要插入的距离,当第i个数移到前面后,后面的数移到前面要插入的距离减一,所以修改树状数组c[]的的值。注意:主要循环i:N~1,先将大的数往前移动,如果需要移动步数太多则跳过。
代码如下:
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> using namespace std; typedef long long LL; int N; int a[100005]; int b[100005]; int p[100005]; int c[100005]; int Lowbit(int t) { return t&(t^(t-1)); } int sum(int x) { int sum=0; while(x>0) { sum+=c[x]; x-=Lowbit(x); } return sum; } void adjust(int li,int t) { while(li<=N) { c[li]+=t; li=li+Lowbit(li); } } int main() { int K; while(scanf("%d%d",&N,&K)!=EOF) { memset(c,0,sizeof(c)); for(int i=1;i<=N;i++) { int x; scanf("%d",&x); a[i]=x; p[x]=i; adjust(i,1); } int tot=1; for(int x=N;x>=1;x--) { if(a[p[x]]==0) continue; if(K<=0) break; int tmp=sum(p[x])-1;///因为前面已经有了tot-1个数,所以距离插入位置边近; if(tmp>K) continue; K-=tmp; b[tot++]=x; a[p[x]]=0; adjust(p[x],-1); if(tmp==0)///不能每次都跳到x=N,否则会超时; x=N; } for(int i=1;i<=N;i++) { if(a[i]) b[tot++]=a[i]; } for(int i=1;i<=N;i++) printf("%d\n",b[i]); } return 0; }