#2428. 七十和十七(xvii)

题目描述
七十君最近爱上了排序算法,于是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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值