本文主要是介绍2023NOIP A层联测6 T2 冒泡排序趟数期望(bubble),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
对于排列 a [ 1 … n ] a[1\dots n] a[1…n],其进行一趟冒泡排序的代码如下:
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 取模。
一般来说,冒泡排序和逆序对有很大的关系。
定义 g i = ∑ j = 1 i − 1 [ a j > a i ] g_i=\sum\limits_{j=1}^{i-1}[a_j>a_i] gi=j=1∑i−1[aj>ai], g i g_i gi 的意义是位置在 i i i 前面,比 a i a_i ai 大的数的个数。
g i g_i gi 有很好的性质:
- g i ∈ [ 0 , i − 1 ] g_i\in[0,i-1] gi∈[0,i−1]
- 每一个 g g g 都唯一对应一个 f f f。(可以从后往前由 g g g 构造出 f f f)
由性质 2 2 2 得到 g i g_i gi 能取遍 [ 0 , i − 1 ] [0,i-1] [0,i−1] 所有值。
模拟一下一趟冒泡排序的过程。对于一个 a i a_i ai,如果 g i g_i gi 不为 0 0 0,则前面比 a i a_i ai 大的某个数一定会与 a i a_i ai 交换,从而 g i g_i gi 减少 1 1 1。
排列有序后, g i g_i gi 都为 0 0 0。要让所有 g i g_i gi 变为 0 0 0,必然进行了至少 max ( g i ) \max(g_i) max(gi) 次排序。所以 r e s = max i = 1 n ( g i ) res=\max\limits_{i=1}^n(g_i) res=i=1maxn(gi)
问题转换为枚举 r e s = k ( 1 ≤ k ≤ n ) res=k(1\le k\le n) res=k(1≤k≤n),求满足 max i = 1 n ( g i ) = k \max\limits_{i=1}^n(g_i)=k i=1maxn(gi)=k 的 g g g 的个数 c n t k cnt_k cntk。
考虑构造 g g g。前面 k k k 个位置可以任取,后面的位置 g i g_i gi 要小于等于 k k k,至少要有一个 g i g_i gi 等于 k k k。
即 c n t k = k ! ( ( k + 1 ) n − k − k n − k ) cnt_k=k!((k+1)^{n-k}-k^{n-k}) cntk=k!((k+1)n−k−kn−k)
答案即为 1 n ! ∑ k = 1 n k ⋅ c n t k \frac1{n!}\sum\limits_{k=1}^nk\cdot cnt_k n!1k=1∑nk⋅cntk
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
int n;
ll f[1000001],inv[1000001];
ll ksm(ll a,ll b)
{ll ans=1;while(b){if(b&1) ans=ans*a%mod;b>>=1;a=a*a%mod;}return ans;
}
int main()
{freopen("bubble.in","r",stdin);freopen("bubble.out","w",stdout);f[0]=1;scanf("%d",&n);for(int i=1;i<=n;i++) f[i]=f[i-1]*i%mod;inv[n]=ksm(f[n],mod-2);for(int i=n-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod;ll ans=0;for(int i=1;i<=n;i++){ans=(ans+i*f[i]%mod*(ksm(i+1,n-i)-ksm(i,n-i)+mod))%mod;}cout<<ans*inv[n]%mod;
}
这篇关于2023NOIP A层联测6 T2 冒泡排序趟数期望(bubble)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!