日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

POJ - 2299 Ultra-QuickSort(线段树+离散化/归并排序)

發布時間:2024/4/11 编程问答 56 豆豆
生活随笔 收集整理的這篇文章主要介紹了 POJ - 2299 Ultra-QuickSort(线段树+离散化/归并排序) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目鏈接:點擊查看

題目大意:給出n個數字,求使用冒泡排序所需要交換的次數

題目分析:這個題n給到了5e5,如果直接冒泡排序的話,的時間復雜度肯定就TLE了,所以不能直接暴力模擬

我們換個思路,這個題其實需要轉化一下,就是求每個位置上數字的逆序數,然后加和就是所要求的答案了

因為相對較大的數字經過排序后要往右邊移動,而任意位置的一個數字所要移動的距離就是他的逆序數,即需要和每一個他右邊

比他小的數字所交換一次,所以現在問題轉化為了求這串數字的逆序數了


那么該怎么求逆序數呢?我們就要用到線段樹了,我先來大體描述一下該怎么用線段樹求逆序數吧:

我們先要知道求一個位置上的逆序數,有兩種途徑,一種是通過求該位置之前比它本身大的數字的個數,另一種就是通過求該位

置之后比它本身小的數字的個數

我們先假設一個數列為5,1,3,2,4,我們需要求這個數列每一項的逆序數,我們就用第一種方法來求吧,就是求每個位置之前比它

本身大的數字的個數,首先我們先假設下面五個方塊依次代表著沒一個數字,點亮后的方塊代表前面已經遍歷過的數字

□? □? □? □? □

一開始還沒有進行填充,我們現在開始遍歷一邊這個數列:

首先第一個數字是5,我們需要查找比5大的數字,即查找區間(5+1,5),很顯然這個區間是不存在的,因為左端點大于了右端

點,故第一個數的逆序數為0,在遍歷下一個數字之前,需要將第一個數字點亮,即變成這個樣子。

□? □? □? □? ■

然后我們再處理第二個數字,我們需要找到比1大的數字,即查找區間(1+1,5)中有多少個點亮的方塊,很顯然答案是1,然后

更新點亮的狀態,將第一個燈也點亮

■? □? □? □? ■

接下來處理第三個數字,是3,那么我們需要查找區間(3+1,5),答案是1,更新狀態:

■? □? ■? □? ■

然后是處理數字2,我們需要查找區間(2+1,5),答案是2,更新狀態:

■? ■? ■? □? ■

最后只剩一個4了,我們需要查找區間(4+1,5),答案是1,更新狀態:

■? ■? ■? ■? ■

到此為止,我們將上述結果加和,得到的便是這一串數列的逆序數了,即0+1+1+2+1=5

然后稍微總結一下,關于上述的兩個操作:“查找區間”和“點亮狀態”的操作,分別對應著線段樹中的區間查找和點更新,是不是有

點感覺了?

等等等等!這個題到這里還沒完呢,為什么呢?因為這個題的數據范圍,n是5e5,還不算太大,那么每一個位置上的數呢?范圍

竟然是999999999?我們都知道線段樹是根據范圍來開的,如果按照范圍的話,我們需要開至少4e10的數組才能實現上述的方

法,不過很顯然,會爆內存,那我們該怎么處理呢?

這個題涉及到了二維偏序問題,在這個題目中,每個數字的位置代表一個維度,每個數字的數值代表著另一個維度,因為他的數

值涉及到的范圍很廣,所以我們需要將其離散化來處理,即在存儲每一個數字的時候一起存上他原本的位置,然后對于他的數值

排序,然后對于他的位置來求逆序數,這樣就可以將求逆序數的范圍規范到了1~n之間了,剩下的就可以用線段樹來解決了。


?2019.11.29更新:

學習了歸并排序的原理和內部實現后,發現這個題目用歸并排序能以穩定的nlogn的時間復雜度解決,掛一下代碼

上代碼:更多的會在代碼里注釋

線段樹:

#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<algorithm> #include<stack> #include<queue> #include<map> #include<sstream> #include<cmath> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=5e5+100;struct Node {int l,r,sum; }tree[N<<2];struct node {int pos,val; }a[N];void build(int k,int l,int r) {tree[k].l=l;tree[k].r=r;tree[k].sum=0;if(l==r){ return;}int mid=(l+r)>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r); }void add(int k,int pos) {if(tree[k].l==tree[k].r){tree[k].sum=1;return;}int mid=(tree[k].l+tree[k].r)>>1;if(mid>=pos)add(k<<1,pos);elseadd(k<<1|1,pos);tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum; }int query(int k,int l,int r) {if(l>r)return 0;if(tree[k].r<l||tree[k].l>r)return 0;if(tree[k].r<=r&&tree[k].l>=l)return tree[k].sum;return query(k<<1,l,r)+query(k<<1|1,l,r); }bool cmp(node a,node b) {return a.val<b.val; }int main() { // freopen("input.txt","r",stdin);int n;while(scanf("%d",&n)!=EOF&&n){build(1,1,n);for(int i=1;i<=n;i++){scanf("%d",&a[i].val);a[i].pos=i;}sort(a+1,a+1+n,cmp);//離散化LL ans=0;//注意這里記得開long long,因為計算的過程中會爆int,因為這個WA了一發for(int i=1;i<=n;i++){ans+=query(1,a[i].pos+1,n);//我們求每個數字前比他大的數字的個數add(1,a[i].pos);//每次處理完記得“點亮”這個點}cout<<ans<<endl;}return 0; }

歸并排序:

#include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=5e5+100;int a[N],b[N];LL merge_sort(int l,int r) {if(l==r)//只有一個元素直接返回return 0;int mid=l+r>>1;LL ans=merge_sort(l,mid)+merge_sort(mid+1,r);//分別往左側和右側遞歸int p=l,q=mid+1;for(int i=l;i<=r;i++)//合并,數組b是輔助數組,輔助排序用的{if(q>r||p<=mid&&a[p]<=a[q])b[i]=a[p++];else{b[i]=a[q++];ans+=mid-p+1;//這里,既然q點屬于當前右區間內最小的點了,那么左區間內比他大的點有mid-p+1個}}for(int i=l;i<=r;i++)a[i]=b[i];return ans; }int main() { // freopen("input.txt","r",stdin); // ios::sync_with_stdio(false);int n;while(scanf("%d",&n)!=EOF&&n){for(int i=1;i<=n;i++)scanf("%d",a+i);printf("%lld\n",merge_sort(1,n));}return 0; }

?

總結

以上是生活随笔為你收集整理的POJ - 2299 Ultra-QuickSort(线段树+离散化/归并排序)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。