「UVA10810」 Ultra-QuickSort 解题报告

题面

看不懂?!

大概的意思就是:

给出一个长度为n的序列,然后每次只能交换相邻的两个数,问最小需要几次使序列严格上升

不断读入n,直到n=0结束

思路:

交换相邻的两个数,这不就类似冒泡排序吗?但是n<500000

~~算了吧,我回去颓A+B ~~

于是我们就发现用冒泡排序直接计算次数是行不通的

但我们要知道:

冒泡排序的交换次数就是序列的逆序对数!!!

所以——就简单了吧~

如何求逆序对?

较easy版的逆序对

1、归并排序

思想是分治法

不断划分为两小段

然后依次由小序列合并为大序列,同时求出逆序数

2、树状数组

因为树状数组的修改查询是log级的所以可以使用(前缀和的方法),没学的建议先去学习一下,不是很难

Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct node{
    int x,i;
}a[500010];
int n;
int b[500010];
int f[500010];
ll ans;
int read()
{
    int s=0;
    char c=getchar();
    while(!isdigit(c))
        c=getchar();
    while(isdigit(c))
    {
        s=(s<<1)+(s<<3)+c-'0';
        c=getchar();
    }
    return s;
}
void update(int x)//修改
{
    for(;x<=n;x+=x&(-x))
        f[x]++;
    return;
}
ll sum(int x)//求前缀和
{
    ll res=0;
    for(;x;x-=x&(-x))
        res+=f[x];
    return res;
}
bool cmp(node a,node b)
{
    return a.x>b.x;
}
int main()
{
    int i,j;
    n=read();
    while(n)
    {
        ans=0;
        memset(f,0,sizeof(f));
        for(i=1;i<=n;i++)
        {
            a[i].x=read();
            a[i].i=i;
        }
        sort(a+1,a+n+1,cmp);
        for(i=1;i<=n;i++)//先hash一下,按数值给其标记,方便后面求逆序对
            b[a[i].i]=i;
        for(i=1;i<=n;i++)
        {
            update(b[i]);//一步一做
            ans+=sum(b[i]-1);
        }
        printf("%lld\n",ans);
        n=read();
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/hovny/p/10133179.html