2023NOIP A层联测6 冒泡排序趟数期望

题目大意

对于一个排列 a 1 , a 2 , … , a n a_1,a_2,\dots,a_n a1,a2,,an,进行一趟冒泡排序的代码为:

for(int i=1;i<n;++i){
if(a[i]>a[i+1]) swap(a[i], a[i+1]);
}

若排列在 k k k趟冒泡排序之后变为有序的,则最小的 k k k定义为 r e s res res

给一个长度为 n n n的随机排列( n ! n! n!种情况等概率出现),请你计算 r e s res res的期望值,对 1 0 9 + 7 10^9+7 109+7取模。

1 ≤ n ≤ 1 0 6 1\leq n\leq 10^6 1n106


题解

有一道思想类似的题,大家感兴趣的话可以看一下:8ady。

对于每趟冒泡排序,都是将一个数放在它后面离他最近的比它大的数前面。然而,我们并不知道会向后移多少,所以我们需要换一个方向思考。

对于所有满足 a i < i a_iai<i i i i,最后 a i a_i ai肯定是要被移到第 a i a_i ai个位置。因为 a i < i a_iai<i,所以一定存在若干个(有可能只有一个,但至少有一个) j < i jj<i满足 a j > a i a_j>a_i aj>ai,那么这些 j j j中肯定有一个 j j j在当前这这趟冒泡排序中与 i i i互换,使得数字 a i a_i ai被往前移了一个位置。所以,对于所有 a i < i a_iai<i,每趟冒泡排序一定且只会使数字 a i a_i ai往前移一个位置,那么

r e s = max ⁡ i = 1 n { i − a i } res=\max\limits_{i=1}^n\{i-a_i\} res=i=1maxn{iai}

那么,对于我们需要算出对于每个 0 ≤ k < n 0\leq k0k<n r e s = = k res==k res==k的序列 a i a_i ai的数量。

但是,直接计算 r e s = = k res==k res==k的序列 a i a_i ai的数量不太好算,我们不妨来求 r e s ≤ k res\leq k resk的序列 a i a_i ai的数量。

我们试着构造序列 a a a,也就是将每个 1 ≤ t ≤ n 1\leq t\leq n 1tn填入序列 a a a r e s ≤ k res\leq k resk,即 ∀ 1 ≤ i ≤ n \forall 1\leq i\leq n ∀1in,都满足 i − a i ≤ k i-a_i\leq k iaik,也就是 i ≤ k + a i i\leq k+a_i ik+ai。那么,将 t t t填到 a i a_i ai中时(也就是令 a i = t a_i=t ai=t),要满足 i ≤ k + t i\leq k+t ik+t,也就是说 t t t只能填到 [ 1 , min ⁡ ( n , k + t ) ] [1,\min(n,k+t)] [1,min(n,k+t)]上。

我们从 t = 1 t=1 t=1 n n n t t t填入 a a a中。对于每个 t t t,它本来是可以填 min ⁡ ( n , k + t ) \min(n,k+t) min(n,k+t)个位置的,但前面 t − 1 t-1 t1个数可以填的区间都在 [ 1 , min ⁡ ( n , k + t ) ] [1,\min(n,k+t)] [1,min(n,k+t)]上,所以可填的位置只有 min ⁡ ( n , k + t ) − ( t − 1 ) \min(n,k+t)-(t-1) min(n,k+t)(t1)

那么, r e s ≤ k res\leq k resk的序列 a a a的个数为 ∏ t = 1 n [ min ⁡ ( n , k + t ) − ( t − 1 ) ] = ( ∏ t = 1 n − k k + 1 ) × ( ∏ t = n − k + 1 n n − t + 1 ) = k ! × ( k + 1 ) n − k \prod\limits_{t=1}^n[\min(n,k+t)-(t-1)]=(\prod\limits_{t=1}^{n-k}k+1)\times(\prod\limits_{t=n-k+1}^nn-t+1)=k!\times (k+1)^{n-k} t=1n[min(n,k+t)(t1)]=(t=1nkk+1)×(t=nk+1nnt+1)=k!×(k+1)nk

V ( k ) = k ! × ( k + 1 ) n − k V(k)=k!\times (k+1)^{n-k} V(k)=k!×(k+1)nk,设 v k v_k vk表示 r e s = = k res==k res==k的序列 a i a_i ai的数量,则 v k = V ( k ) − V ( k − 1 ) v_k=V(k)-V(k-1) vk=V(k)V(k1)

于是,答案为 ∑ k = 0 n − 1 k × v k n ! \dfrac{\sum\limits_{k=0}^{n-1}k\times v_k}{n!} n!k=0n1k×vk

时间复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn)

code

#include
using namespace std;
const long long mod=1e9+7;
const int N=1e6;
int n;
long long ans=0,jc[N+5],ny[N+5],v[N+5];
long long mi(long long t,long long v){
	if(!v) return 1;
	long long re=mi(t,v/2);
	re=re*re%mod;
	if(v&1) re=re*t%mod;
	return re;
}
void init(){
	jc[0]=1;
	for(int i=1;i<=N;i++) jc[i]=jc[i-1]*i%mod;
	ny[N]=mi(jc[N],mod-2);
	for(int i=N-1;i>=0;i--) ny[i]=ny[i+1]*(i+1)%mod;
}
int main()
{
	freopen("bubble.in","r",stdin);
	freopen("bubble.out","w",stdout);
	init();
	scanf("%d",&n);
	for(int k=0;k<n;k++){
		v[k]=jc[k]*mi(k+1,n-k)%mod;
	}
	for(int k=n-1;k>=0;k--){
		v[k]=(v[k]-v[k-1]+mod)%mod;
		ans=(ans+k*v[k]%mod)%mod;
	}
	ans=ans*ny[n]%mod;
	printf("%lld",ans);
	return 0;
}

你可能感兴趣的:(题解,题解,c++)