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

2023-10-10 11:05

本文主要是介绍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_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{iai}

那么,对于我们需要算出对于每个 0 ≤ k < n 0\leq k<n 0k<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<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 冒泡排序趟数期望的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/180019

相关文章

冒泡排序——基于Java的实现

简介    冒泡排序(Bubble Sort)是一种简单的排序算法,适用于小规模数据集。其基本思想是通过重复遍历待排序的数组,比较相邻的元素并交换它们的位置,以此将较大的元素逐步“冒泡”到数组的末尾。算法的名称源于其运行过程中,较大的元素像水中的大气泡一样逐渐浮到顶部。  排序过程   for (int i = 0; i < num.length - 1; i++) {

BubbleSort(冒泡排序)

平均时间 复杂度 最差时间 复杂度 最佳时间 复杂度 空间复杂度 O(n^2) O(n^2) O(n) O(1) 稳定 public static void bubbleSort( int[] arr ) {if( arr == null | arr.length < 2 ) {return;}for( int j = arr.length - 1; j > 0;

冒泡排序【BubbleSort】

冒泡排序 假设初始的数组是[5,4,7,2] 以从小到大排序为例: 将第0个元素与第一个元素进行比较, 5 > 4, 所以交换位置, 此时[4,5,7,2] 将第1个元素与第二个元素进行比较, 5 < 7, 所以保持,此时[4,5,7,2] 将第2个元素与第三个元素进行比较, 7 > 2, 所以交换位置, 此时[4,5,2,7] 这样就经过了一轮的冒泡,最后一个元素就是最大的元素了。

冒泡排序和鸡尾酒排序(code)

昨天回顾了下冒泡排序和鸡尾酒排序,用面向对象的方式写了一下,并且优化了代码,记录一下~ 一、冒泡排序 # 冒泡排序class BubbleSort(object):def __init__(self, data_list):self.data_list = data_listself.length = len(data_list)# 简单粗暴的排序方式def b_sort(self):d

冒泡排序算法及其简单优化(基于Java)

冒泡排序算法通过多次比较和交换来实现排序,其流程如下: (1)对数组中的各数据,依次比较相邻两个元素的大小。 (2)如果前面的数据大于后面的数据,就交换这两个数据。通过第一轮的多次比较排序后,便可将最小的数据排好。 (3)再用同样的方法把剩下的数据逐个进行比较,最后便可按照从小到大的顺序排好数组的各数据。 所谓“冒泡”,就是大数沉下去(数组的底部),小数相应的浮上来(数组的顶部)。

Java基础07 数组算法(顺序查找、冒泡排序、选择排序、二分查找)

超详细的Java知识点路线图 前言 知道了怎么使用数组后,还需要结合数组和前面的知识,解决某些实际的问题。 本文我们将学习数组的常用算法:求最大值、顺序查找、二分查找、冒泡排序、选择排序。 如果能掌握这些算法,那么大家的编程能力会得到很大增强哦。 求最大值 给定一个数组,求出所有数据中最大(最小)的数据 算法描述: 定义最大值变量,将数组中第一个数据赋值给最大值从数组的第二个数据开始

概率、期望

UVA1636 决斗 Headshot #include <bits/stdc++.h>using namespace std;char s[110];int shoot, no, len;int main(){while(scanf("%s", &s)!=EOF){shoot=0;no=0;len=strlen(s);s[len]=s[0]; for(int i=0; i<len;

概率学 笔记一 - 概率 - 随机变量 - 期望 - 方差 - 标准差(也不知道会不会有二)

概率不用介绍,它的定义可以用一个公式写出: 事件发生的概率 = 事件可能发生的个数 结果的总数 事件发生的概率=\cfrac{事件可能发生的个数}{结果的总数} 事件发生的概率=结果的总数事件可能发生的个数​ 比如一副标准的 52 张的扑克牌,每张牌都是唯一的,所以,抽一张牌时,每张牌的概率都是 1/52。但是有人就会说了,A 点明明有四张,怎么会是 1/52 的概率。 这就需要精准的指出

android 算法可视化(1) --冒泡排序可视化实现

前言 以前写了很多算法相关的博客,每篇博客都会用word或者processing画上很多图,非常浪费时间,那时候就一直有考虑能不能使用程序来实现这种过程,不仅不用自己画那么图,而且编程实现可视化的话,还可以动态更清晰的表现算法的过程。于是查找了相关的资料和自己对算法的理解先实现一个冒泡排序的可视化,代码是Android的。 效果 实现 要实现这个动画效果,实际上需要两个基本的模块组成:

冒泡排序;选择排序;插入排序;快排;判断大小端;位运算

1.冒泡排序:基础        时间复杂度来说:o(n^2) 从左到右,相邻元素进行比较。每次比较一轮,就会找到序列中最大的一个或最小的一个。这个数就会从序列的最右边冒出来。 #include <stdio.h>int main(void){int str[32] = 0;int i = 0;int j = 0;int len = sizeof(str) / sizeof(str[0]);