[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

 

 

posted @ 2017-10-13 19:03  rvalue  阅读(502)  评论(0编辑  收藏  举报