题意:模拟一个序列的冒泡排序,交换k次以后的序列,n<=1e6,交换次数<=10^12。
很久没打题啥都不会了,只能看题解。。
感觉这题虽然是模拟,但是并不简单。。可能是我水了。
就按照题目给出的代码来模拟。
首先二分出外层的循环次数,然后注意到冒泡的规律,即只有大于后一个数字的才会交换,那么求出b[i]表示i前面有多少个比他大,假设有x次外层,那么总次数就是
∑min(b[i],x)
,注意到b[i]<=x的话,这个数已经排序完毕了。
对于b[i]>x的部分,他们相对于原数列的位置不变,然后就可对外层进行处理了。。
然后剩下的就是内层的了,内层的由于次数<=1e6所以直接模拟即可,细节看代码吧。
#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e6+5;
typedef long long ll;
int n;
ll m;
int a[N],t[N],b[N];
int ans[N],c[N];
inline int lowbit(int x)
{
return x&(-x);
}
inline void add(int x,int y)
{
while(x<=n)
{
t[x]+=y;
x+=lowbit(x);
}
}
inline int ask(int x)
{
int ans=0;
while (x>0)
{
ans+=t[x];
x-=lowbit(x);
}
return ans;
}
inline int find()
{
int l=1,r=n;
ll ans=0;
while (l<=r)
{
int mid=(l+r)>>1;
ans=0;
fo(i,1,n)ans+=1ll*min(mid,b[i]);
if (ans<=m)l=mid+1;
else r=mid-1;
}
return r;
}
int main()
{
scanf("%d%lld",&n,&m);
fo(i,1,n)scanf("%d",&a[i]);
fo(i,1,n)
{
b[i]=ask(n)-ask(a[i]);
add(a[i],1);
}
int wc=find();
ll tot=0;
fo(i,1,n)tot+=1ll*min(wc+1,b[i]);
ll nc=m-tot;
if (nc>0)
{
printf("Impossible!\n");
return 0;
}
tot=0;
fo(i,1,n)tot+=min(wc,b[i]);
m=m-tot;
int cnt=0;
fo(i,1,n)
{
if (b[i]>wc)ans[i-wc]=a[i];
else c[++cnt]=a[i];
}
sort(c+1,c+1+cnt);
cnt=0;
fo(i,1,n)
{
if (!ans[i])
ans[i]=c[++cnt];
}
for(int i=1;i<n&&m;i++)
{
if (ans[i]>ans[i+1])
{
swap(ans[i],ans[i+1]);
m--;
}
}
printf("%d",ans[1]);
fo(i,2,n)printf(" %d",ans[i]);
return 0;
}