题意
一个这样的算法:
int cnt=0; for(int i=1;i<=n;++i) { for(int j=i+1;j<=n;++j) { if(a[j]<a[i]) swap(a[j],a[i]); ++cnt; } }
现在你cnt和原始数组a,求cnt步数后a的结果。注意a是n排列。
$1 leq n leq 10^6,1 leq cnt leq frac{n(n-1)}{2}$
思考
对于某个i,如果j从i+1循环到了n,那么含义就是将i~n中最小的数放到第i位上,剩下的访问过的向右循环位移一位(在访问过的数中循环位移)。
如果说外层的i在1~k中都完全遍历了一边,那么含义就是将1~k放到最前面。
如果我们从小到大依次插入数字i,可以发现,最后每个数字的相对位置是不会改变的。
这样,我们模拟这个过程。首先确定每个数的位置在哪,将大小位1~k的位置扔到大根对中。接下来从k+1枚举到n,若数字i的位置比堆顶靠后,那么这个数的位置就不会有变化(因为比它小的数都在它之前)。否则这个数最后就会到堆顶的位置(注意是从k+1枚举到n,从小到大),将对顶弹出,把数字i的位置加入。
最后将剩下的步数处理了。
复杂度$O(nlogn)$
代码
1 // luogu-judger-enable-o2 2 #include<bits/stdc++.h> 3 using namespace std; 4 typedef long long int ll; 5 const int maxn=1E6+5; 6 int n,a[maxn],b[maxn],where[maxn]; 7 ll cnt; 8 void solve(int x) 9 { 10 if(x==0) 11 return; 12 priority_queue<int>Q; 13 for(int i=1;i<=n;++i) 14 where[a[i]]=i; 15 for(int i=1;i<=x;++i) 16 { 17 b[i]=i; 18 Q.push(where[i]); 19 } 20 for(int i=x+1;i<=n;++i) 21 { 22 if(where[i]>Q.top()) 23 b[where[i]]=i; 24 else 25 { 26 b[Q.top()]=i; 27 Q.pop(); 28 Q.push(where[i]); 29 } 30 } 31 for(int i=1;i<=n;++i) 32 a[i]=b[i]; 33 } 34 int main() 35 { 36 // freopen("sort.in","r",stdin); 37 // freopen("sort.out","w",stdout); 38 ios::sync_with_stdio(false); 39 cin>>n>>cnt; 40 for(int i=1;i<=n;++i) 41 cin>>a[i]; 42 for(int i=1;i<=n;++i) 43 { 44 if(cnt<=n-i) 45 { 46 solve(i-1); 47 for(int j=i+1;j<=n;++j) 48 { 49 if(a[j]<a[i]) 50 swap(a[j],a[i]); 51 --cnt; 52 if(cnt==0) 53 { 54 for(int k=1;k<=n;++k) 55 cout<<a[k]<<" "; 56 cout<<endl; 57 return 0; 58 } 59 } 60 } 61 else 62 cnt-=n-i; 63 } 64 return 0; 65 }