bzoj3580 冒泡排序 模拟

该博客主要讨论了如何模拟冒泡排序的过程,特别是在交换次数限制为10^12的情况下,如何通过二分法确定外层循环次数,并利用冒泡排序的特性优化计算。博主分享了如何计算每个元素在排序后的位置,以及如何处理内外层循环的细节,适用于大规模序列排序问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:模拟一个序列的冒泡排序,交换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;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值