本文主要是介绍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 1≤n≤106
题解
有一道思想类似的题,大家感兴趣的话可以看一下:8ady。
对于每趟冒泡排序,都是将一个数放在它后面离他最近的比它大的数前面。然而,我们并不知道会向后移多少,所以我们需要换一个方向思考。
对于所有满足 a i < i a_i<i ai<i的 i i i,最后 a i a_i ai肯定是要被移到第 a i a_i ai个位置。因为 a i < i a_i<i ai<i,所以一定存在若干个(有可能只有一个,但至少有一个) j < i j<i j<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_i<i ai<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{i−ai}
那么,对于我们需要算出对于每个 0 ≤ k < n 0\leq k<n 0≤k<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 res≤k的序列 a i a_i ai的数量。
我们试着构造序列 a a a,也就是将每个 1 ≤ t ≤ n 1\leq t\leq n 1≤t≤n填入序列 a a a。 r e s ≤ k res\leq k res≤k,即 ∀ 1 ≤ i ≤ n \forall 1\leq i\leq n ∀1≤i≤n,都满足 i − a i ≤ k i-a_i\leq k i−ai≤k,也就是 i ≤ k + a i i\leq k+a_i i≤k+ai。那么,将 t t t填到 a i a_i ai中时(也就是令 a i = t a_i=t ai=t),要满足 i ≤ k + t i\leq k+t i≤k+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 t−1个数可以填的区间都在 [ 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)−(t−1)。
那么, r e s ≤ k res\leq k res≤k的序列 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=1∏n[min(n,k+t)−(t−1)]=(t=1∏n−kk+1)×(t=n−k+1∏nn−t+1)=k!×(k+1)n−k
设 V ( k ) = k ! × ( k + 1 ) n − k V(k)=k!\times (k+1)^{n-k} V(k)=k!×(k+1)n−k,设 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(k−1)。
于是,答案为 ∑ k = 0 n − 1 k × v k n ! \dfrac{\sum\limits_{k=0}^{n-1}k\times v_k}{n!} n!k=0∑n−1k×vk。
时间复杂度为 O ( n log n ) O(n\log n) O(nlogn)。
code
#include<bits/stdc++.h>
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;
}
这篇关于2023NOIP A层联测6 冒泡排序趟数期望的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!