题目描述
七十君最近爱上了排序算法,于是Ta让十七君给Ta讲冒泡排序。
十七君给七十君讲完了冒泡排序以后,七十君回家苦思冥想,又创造了一种名为七十排序的算法。下面是这个算法排序一个排列的过程:
首先从左到右扫描每个相邻数对。如果这两个数是逆序的,则将第二个数(也就是小的数)放在整个排列的开头,其他数位置不变,并把计数器加一。如果没有逆序的相邻数对了,就说明已经排好序了,算法终止。
七十君认为计数器的值反映了这个算法的运行时间。但十七君觉得七十君发明的这个算法会很慢,所以他请你帮忙算算,对于所有长度为n的排列P,
[E(n)=\frac{\sum{f§}}{n!}]
的值,这里f§表示排列P运行算法结束时计数器的值。
输入格式
一行一个整数n。
输出格式
如果E(n)=a/b,求c使得 bc=a \ (mod~10^9+7) bc=a (mod 10
9
+7)
并输出,其中 0 \le c < 10^9+7 0≤c<10
9
+7 ,如果c不存在输出-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。
对于100%的数据,n ≤ 105。
来源
东师附中模拟卷
题解:
由题意得:
假设当前在比较a[i]和a[i+1]。那么可知1- -i必然有序。
首先,先证明把1- -i+1调成有序对于a[i+1]需要比较多少次。
设f[i]为把权值为i的数从序列尾调到有序时的步数(序列除最后的i其它都是有序的。
那么模拟过程可得
f[1]=1;
f[i]=f[1]+f[2]+...+f[i-1];
(打表) 可得
f[i]=power(2,i-1);
所以对于每一个在序列末尾的数,权值为i,所花费的代价是f[i]。
再者,设t[i]表示对于长度为i的序列,(只要权值不重复就行,什么权值都无所谓,这个要记牢),的全排列所需的步数。
所以可得
t[i]=i*t[i-1]+(f[1]+f[2]+f[3]+...+f[i-1])*(i-1)!;
解释一下,第一项是枚举第i位为1—>i时,前面i-1个数的步数(因为权值不同,步数一样)。
第二项是枚举第i个数在所有数中排名为j时,步数为f[j]。又因为是全排列,所以是f[j]*(i-1)!。
因为
f[i]=f[1]+f[2]+...+f[i-1];
所以
t[i]=i*t[i-1]+f[i]*(i-1)!;
t[i]=i*t[i-1]+power(2,i-1)*(i-1)!;
代码来源@zengxingzhou
#include <iostream>
#include <cctype>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const long long mod = 1000000007;
long long C[100010], ans[100010], two_pow[100010];
inline long long fst_two_power(long long n) {
if (two_pow[n] != 0)
return two_pow[n];
return two_pow[n] = (fst_two_power(n - 1) * 2) % mod;
}
inline long long fst_power(long long n, long long m) {
long long re = 1;
while (n) {
if (n & 1)
re = (re * m) % mod;
n >>= 1;
m *= m;
m %= mod;
}
return re;
}
inline long long get_sigma_f(int n) {
if (n == 1)
return 0;
if (ans[n] != 0)
return ans[n];
return ans[n] = ((get_sigma_f(n - 1) * n) % mod + (fst_two_power(n - 1) - 1) * C[n - 1] % mod) % mod;
}
int main() {
int n;
scanf("%d", &n);
C[1] = 1;
two_pow[0] = 1;
for (int i = 2; i <= n; i++)
C[i] = (i * C[i - 1]) % mod;
printf("%lld\n", (get_sigma_f(n) % mod * fst_power(mod - 2, C[n]) % mod));
return 0;
}
仔细分析可得
我还不知道为什么。。。
如下代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define N 100005
using namespace std;
const long long mod=1000000007;
long long n;
inline long long kuai(long long x,long long c){
long long num=1;
while(c){
if(c&1)num=(num*x)%mod;
x=(x*x)%mod;
c>>=1;
}
return num;
}
int main()
{
scanf("%lld",&n);
long long ans=0;
for(int i=1;i<=n;++i){
ans=(ans+(kuai(2,i-1)-1)*kuai(i,mod-2)%mod)%mod;
}
printf("%lld\n",ans);
return 0;
}