[COGS 2066]七十和十七
2066. 七十和十七
★★★ 輸入文件:
xvii.in
輸出文件:xvii.out
簡單對比
時間限制:1 s 內存限制:256 MB【題目描述】
七十君最近愛上了排序算法,於是Ta讓十七君給Ta講冒泡排序。
十七君給七十君講完了冒泡排序以後,七十君回家苦思冥想,又創造了一種名
為七十排序的算法。下面是這個算法排序一個排列的過程:
首先從左到右掃描每個相鄰數對。如果這兩個數是逆序的,則將第二個數(也
就是小的數)放在整個排列的開頭,其他數位置不變,並把計數器加一。如果
沒有逆序的相鄰數對了,就說明已經排好序了,算法終止。
七十君認為計數器的值反映了這個算法的運行時間。但十七君覺得七十君發明
的這個算法會很慢,所以他請你幫忙算算,對於所有長度為n的排列P,
\[E(n)=\frac{\sum f(P)}{n!}\]
的值,這裏f(P)表示排列P運行算法結束時計數器的值。
【輸入格式】
一行一個整數n。
【輸出格式】
如果E(n)=a/b,求c使得
bc 三 a (mod 10^9+7)
並輸出,其中0≤c<10^9+7,如果e不存在輸出-1。
【樣例輸入】
4【樣例輸出】
250000005【提示】
對於排列4 1 3 2,算法結束時計數器的值為5。
4 1 3 2,4和1形成逆序,將1放到排列最前方。
1 4 3 2,4和3形成逆序,將3放到排列最前方。
3 1 4 2,3和1形成逆序,將1放到排列最前方。
1 3 4 2,4和2形成逆序,將2放到排列最前方。
2 1 3 4,2和1形成逆序,將1放到排列最前方。
1 2 3 4,現在排列已經排序完畢。
E(4)=3.25。
數據範圍與約定
對於20%的數據,n≤8。
對於40%的數據,n≤30。
對於60%的數據,n≤200。
對於1OO%的數據,n≤10^5。
題解
首先我們可以發現, 將 $n$ 個數排序的過程可以轉化為按方案排序 $n-1$ 個數後將最後一個數按方案再排進去. 對於長度為 $n$ 的全排列, 若第 $n$ 個數 $a_n=n$ , 則不會引起計數器變動(因為它在前 $n-1$ 個排好序後就已經在最後了), 否則會引起計數器增加 $2^{a_n-1}$ . 枚舉最後加入的數 $a_n$ 即可在 $O(n^2)$ 時間復雜度內解決. 最終表達式為:
\[ans=\sum_{i=1}^n\sum_{j=1}^{i-1}2^{j-1}\]
註意到第二部分求和為等差數列形式, 我們可以通過等差數列求和公式進行計算. 於是上式可以化簡為:
\[ans=\sum{i=1}^n\frac{2^{i-1}-1}{i}\]
參考代碼
GitHub
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <iostream> 5 #include <algorithm> 6 7 const int MOD=1e9+7; 8 9 int Pow(int,int,int); 10 11 int main(){ 12 int n; 13 scanf("%d",&n); 14 int ans=0; 15 for(int i=1;i<=n;i++){ 16 ans=(ans+1ll*(Pow(2,i-1,MOD)-1+MOD)%MOD*Pow(i,MOD-2,MOD)%MOD)%MOD; 17 } 18 printf("%d\n",ans); 19 return 0; 20 } 21 22 int Pow(int a,int n,int p){ 23 int ans=1; 24 while(n>0){ 25 if((n&1)!=0){ 26 ans=1ll*ans*a%p; 27 } 28 a=1ll*a*a%p; 29 n>>=1; 30 } 31 return ans; 32 }Backup
[COGS 2066]七十和十七