CH - 0601 Genius ACM(倍增+归并排序)
題目鏈接:點擊查看
題目大意:
給定一個正整數M,對于任意一個整數集合S,定義“檢驗值”如下:
從集合S中取出M對數(即2*M個數,不能重復使用集合中的數,如果S中的整數不夠M對,則取到不能取為止),使得“每對數的差的平方”之和最大,這個最大值就稱為集合S的“檢驗值”
現在給定一個長度為N的數列A以及一個整數T,我們要把A分成若干段,使得每一段的“檢驗值”都不超過T,求最少要分成幾段
題目分析:因為題意比較難懂,我也不太會總結,就直接比這大藍書的題意抄上去了,然后一步一步分析一下這個題目的方向吧
首先我們可以知道,對于一個整數集合S,若要求檢驗值,我們必須將其排序一下,因為若要讓每對數的差的平方和最大,那肯定優先匹配最大值和最小值,其次匹配次大值和次小值,稍微證明一下:
對于任意組合的平方和可以表示為如下形式
前面的平方和是一定的,若我們想讓整個式子的值盡量大,那么我們就需要讓最大值的系數盡量小,那么顯然最大值與最小值匹配最合理,得證
因為題目中要找原數組中的連續區間,所以我們不妨固定左區間去找最大可能的右區間,對于整個數列求右區間肯定是滿足二分性質的,所以我們可以二分,但是在這個題目中顯然不太合適,因為可能會造成二分了整個區間后,只是讓當前的右端點向右移動了一點點,但是二分第一步就要檢驗(R-L)/2這么一大段,很顯然造成了時間上的浪費,所以我們可以考慮倍增,讓右區間一開始與左區間重合,不斷向右倍增,若滿足條件就擴大倍增指數為兩倍,不滿足的話就縮小兩倍,直到倍增指數為零,說明當前的右端點就是最優解了,大概過程是這樣的:
時間復雜度也是logn級別的,只不過多了一個小常數,因為是要先擴再縮,多了縮這么一步,然后處理每個區間是要先排序再遍歷,最終的時間復雜度為nlogn*logn,這個題目還是不太可行,因為n給到了5e5,按照上面算下來是2e8,雖然給了10秒,但感覺還是比較懸,所以我們可以在排序上優化,因為我們每次check區間的時候,前面的[l,mid]都已經排好序了,無序的只有[mid+1,r]這一段,所以我們不需要每次都nlogn的排序,只需要對無序的部分排序,然后用歸并排序的思想合并起來就好了
這樣優化下來,就能把logn優化掉了,總時間復雜度為nlogn,雖然帶著點小常數,但無傷大雅了已經
注意各個變量記得開longlong,然后就是通過這個題目學了一波merge函數,作用正是將兩個有序容器合并為一個有序容器,在這個題目中可以簡化操作(懶死了)
代碼:
#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> #include<unordered_map> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=5e5+100;int n,m;LL a[N],temp[N],temp1[N],k;//temp[i]代表1~i的有序數組 temp1用于輔助排序bool check(int l,int mid,int r) {for(int i=mid;i<=r;i++)//將原數組的值拿下來并排序temp[i]=a[i];stable_sort(temp+mid,temp+r+1);merge(temp+l,temp+mid,temp+mid,temp+r+1,temp1+l);LL ans=0;for(int i=1;i<=min(m,(r-l+1)/2);i++)//求一下檢驗值ans+=(temp1[l+i-1]-temp1[r-i+1])*(temp1[l+i-1]-temp1[r-i+1]);if(ans<=k)//若滿足條件,返回true并將[l,r]區間更新為有序{for(int i=l;i<=r;i++)temp[i]=temp1[i];return true;}return false; }int main() { // freopen("input.txt","r",stdin); // ios::sync_with_stdio(false);int w;cin>>w;while(w--){scanf("%d%d%lld",&n,&m,&k);for(int i=1;i<=n;i++)scanf("%lld",a+i);int l=1,r=1,p=1,ans=0;temp[1]=a[1];while(r<=n){if(!p){ans++;r++;l=r;p=1;temp[l]=a[l];}else{if(r+p<=n&&check(l,r+1,r+p)){r+=p;p*=2;}elsep/=2;}}printf("%d\n",ans);}return 0; }?
總結
以上是生活随笔為你收集整理的CH - 0601 Genius ACM(倍增+归并排序)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CodeForces - 1168B G
- 下一篇: POJ - 3190 Stall Res