1. 程式人生 > >[COGS 2066]七十和十七

[COGS 2066]七十和十七

pen 冒泡排序 urn enter iostream lose spa display 表示

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]七十和十七