LOJ#6130. 「2017 山东三轮集训 Day1」Fable 树状数组+逆序对
求:对一个数列进行 $k$ 次冒泡排序后的结果.
先考虑数字互不相同的情况.
设 $i$ 位置前大于 $v[i]$ 的数字有 $ma[i]$ 个.
那么,如果 $k \leqslant ma[i]$,则 $i$ 位置的数就会移到 $i-k$.
否则,$i$ 位置的数就会向前移动 $ma[i]$ 次后成为前缀最大值,然后开始向后移动.
对于能确定位置的数字设成第一类,不能确定的设成第二类,那么我们只需确定所有第二类数的位置即可.
对于第二类数字 $x$ 来说,其肯定会在 ma[i] 时刻成为前缀最大值.
然后这个数字就会一直向后移动.
而这个数字要是向后移动的话只可能是后面小于该数的数字移动到该数字前面来.
所以如果一个数在第二类数中成为前缀最大值,该数在第二类数中就始终是前缀最大值.
也就是说,对于第二类数字,不会出现大的出现在小的前面.
那么我们就将第二类数字排序,依次放到空白位置即可.
然后如果有相同数字的话就让前面的数小于后面的数(这样相同的数就不会交换了)
#include <set> #include <cstdio> #include <algorithm> #define N 200009 #define ll long long #define setIO(s) freopen(s".in","r",stdin) using namespace std; int n,K; int a[N],A[N],bu[N],arr[N],id[N],sum[N],pos[N]; int lowbit(int x) { return x&(-x); } void update(int x,int v) { for(;x<N;x+=lowbit(x)) sum[x]+=v; } int query(int x) { int re=0; for(;x;x-=lowbit(x)) re+=sum[x]; return re; } set<int>se; set<int>::iterator it; int main() { // setIO("input"); // freopen("input.out","w",stdout); scanf("%d%d",&n,&K); for(int i=1;i<=n;++i) { scanf("%d",&a[i]); A[i]=a[i]; } sort(A+1,A+1+n); for(int i=1;i<=n;++i) { a[i]=lower_bound(A+1,A+1+n,a[i])-A; } for(int i=1;i<=n;++i) { if(!bu[a[i]]) { bu[a[i]]=a[i]; } else ++bu[a[i]]; arr[i]=bu[a[i]]; id[arr[i]]=A[a[i]]; } for(int i=1;i<=n;++i) { int num=i-1-query(arr[i]); if(K<=num) { pos[i-K]=arr[i]; } else { se.insert(arr[i]); } update(arr[i],1); } for(int i=1;i<=n;++i) { if(pos[i]) printf("%d\n",id[pos[i]]); else { it=se.begin(); printf("%d\n",id[*it]); se.erase(it); } } return 0; }