1. 程式人生 > >「UVA10810」 Ultra-QuickSort 解題報告

「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;
}