题目大意:众所周知冒泡排序算法多数情况下不能只扫描一遍就结束排序,而是要扫描好几遍。现在你的任务是求1~N的排列中,需要扫描K遍才能排好序的数列的个数模20100713。注意,不同于真正的冒泡排序算法,只要数列有序就立刻停止,而不用再检验一遍。

估计多数人都是找规律吧,先看出递推,然后求出通项……这个题只有找出通项公式才能通过,所以首先公布答案:

K!((K + 1) ^ (N - K) - K ^ (N - K))

好吧,现在让我们来证明一下。

首先定义函数d(x),对于1~N的一个排列,d(x)表示第x个数前面有多少个数字大于该数。

比如说对于3 2 4 1 5,有d(1) = 0,d(2) = 1,d(3) = 0,d(4) = 3,d(5) = 0。

现在我们来证明d(x)函数的两条性质:

(一)对于一个排列,对于所有x <= N,有d(x) = 0是这个排列是有序的充要条件。

如果存在1 <= i, j <= N,使得i < j,ai > aj,那么由于aj前面有ai大于它,故d(j) >= 1。而这与d(j) = 0矛盾,反之亦然。所以说命题成立。

(二)冒泡排序的每次扫描的结果是,对于非零的d(x)值,这个位置的d(x)会且只会减少1。

考虑某个非零的d(x)值。由于d(x) >= 1,所以必然存在整数i∈[1, x - 1],满足ai > ax。设m为a1, a2, ..., a(x -1)中的最大值,位置为i,则必有m > ax。那么,在扫描到a(x - 1)和ax的时候,由于前面的交换,必然有a(x - 1) = m。

原因是如果前面的交换将m交换到了a(x - 2)的位置,那么由于m > a(x - 1),那么ai必然能够被交换到a(x - 1)的位置。故由数学归纳法,只要m能够被交换到a(i + 1)即可。而在交换之前a(i - 1) < m,m与a(i - 1)不发生交换;同时必然有m > a(i + 1),m一定会被交换到a(i + 1),故该结论成立,从而扫描a(x - 1)和ax的时候a(x - 1) = m。

这时由于m > ax,m与ax之间要交换,交换的效果由于ax前面比ax小的数字减少了一个,d(x)减小了1。所以说d(x)在这个过程中必会减少。

而另一方面,完成了m与ax的这次交换之后,这一次扫描显然就不会再交换ax的值了(这时ax位置上的值已经是m了)。所以说,d(x)也只能减少1。这就证明完毕。

证明了d(x)函数的这个性质之后,我们就可以得出对于1~n的一个排列,它所需要的冒泡排序的扫描次数为

K = max (d(i), 1 <= i <= N)

而这个结论很显然,因为只有经过K次扫描,所有位置的d值才能都变为0。

到此,我们成功地将冒泡排序的次数问题转化为d(x)值满足条件的数列的问题。原问题也就转化成了有多少个排列使得其中最大的d(x)值恰好为K。然而这也是复杂的,所以说我们不妨先解决有多少个排列使得其中最大的d(x)值不大于K。

首先可以确定N >= K + 1,否则不可能出现某个位置前面有K个数大于它。

然后决定原数列中1的位置。显而易见,如果最小数的位置为x,则其d(x) = x - 1。而d(x) <= K,故x <= K + 1,也就是说1有K + 1种放置方法;而放置2的时候,我们完全可以考虑一个新的排列2~N,这时2有K + 1种放置方法,然后再把1插到位置1~K + 1,而不影响其它数的d值。所以说,前N - K个数的放置方法的种类有

(K + 1) ^ (N - K)

之后只需要考虑N - K + 1 ~ N的排列即可。然而,由于整个数列只有K个数字,不可能出现某个d值大于K + 1。所以说排列方法有K!种。故,所有位置d值不大于K的排列的方案数有

K!((K + 1) ^ (N - K))

但是这是不大于K的排列数量,恰好为K的有怎么办呢?很简单,只需要减去不大于K - 1的排列数量便可。所以最后的答案为

K!((K + 1) ^ (N - K)) - (K - 1)!(K ^ (N - K + 1))

化简之后我们就得到

K!((K + 1) ^ (N - K) - K ^ (N - K))

这就是原来的式子,它的正确性就证明完毕。

然后代码就是小意思了:

#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;

const int mod = 20100713;

__int64 power (int k, int n)
{
    __int64 r = k, ans = 1;
    while (n)
    {
        if (n & 1) ans = (ans * r) % mod;
        r = (r * r) % mod;
        n >>= 1;
    } return ans;
}

__int64 factorial[1000010];

int main ()
{
    int t, n, k;
    __int64 ans;
    factorial[0] = 1;
    for (int i = 1; i < 1000010; i++)
        factorial[i] = (factorial[i - 1] * i) % mod;
    scanf("%d", &t);
    for (; t > 0; t--)
    {
        scanf("%d %d", &n, &k);
        n -= k;
        ans = (power(k + 1, n) - power(k, n)) % mod;
        if (ans < 0) ans += mod;
        ans = (ans * factorial[k]) % mod;
        printf("%I64d\n", ans);
    }
    return 0;
}

转载于:https://www.cnblogs.com/767355675hutaishi/p/3873770.html

POJ 3761:Bubble Sort——组合数学相关推荐

  1. POJ 3761 Bubble Sort(乘方取模)

    点我看题目 题意 : 冒泡排序的原理众所周知,需要扫描很多遍.而现在是求1到n的各种排列中,需要扫描k遍就变为有序的数列的个数,结果模20100713,当然了,只要数列有序就扫描结束,不需要像真正的冒 ...

  2. 数论三之组合数学Ⅰ-Max-Min Sums,Binomial Coefficient is Fun,Strivore,Bubble Sort,放棋子,LOJ6671,Iroha and a Grid

    组合计数我最爱 Max-Min Sums description solution code Binomial Coefficient is Fun description solution code ...

  3. 排序 时间倒序_经典排序算法之冒泡排序(Bubble Sort)

    冒泡排序 ( Bubble Sort ) 冒泡排序,正如它的名字一样,未排序数组中的最大(小)值会依次往上浮.冒泡排序主要有两个基本步骤:相邻元素之间的比较 和 交换位置. 步骤分析: 令待排序序列为 ...

  4. Recursive Bubble Sort(递归冒泡排序)

    程序来源:Recursive Bubble Sort 迭代冒泡排序算法: // Iterative Bubble Sort bubbleSort(arr[], n) {for (i = 0; i &l ...

  5. 冒泡排序的PHP实现 Bubble Sort

    冒泡排序Bubble Sort的PHP实现.代码中函数说明: out_arr,用于将数组输出成一个字符串,以便查看 bubblesort,第一种实现方案,从后往前依次选出需要的值,这里是较大的 bub ...

  6. 2016 Multi-University Training Contest 4 Bubble Sort(树状数组模板)

    Bubble Sort 题意: 给你一个1~n的排列,问冒泡排序过程中,数字i(1<=i<=n)所到达的最左位置与最右位置的差值的绝对值是多少 题解: 数字i多能到达的最左位置为min(s ...

  7. python冒泡排序_5种python方法实现冒泡排序可视化:Bubble Sort Visualizer

    1 说明: ===== 1.1 冒泡排序: 1.1.1 原理:比较两个相邻的元素,将值大的元素交换至右端. 1.1.2 来源:由来是因为越大的元素会经由交换慢慢"浮"到数列的顶端, ...

  8. 经典排序算法 - 冒泡排序Bubble sort

    经典排序算法 - 冒泡排序Bubble sort 其原理是比较接近的数字22,按照从小到交换大或降序排列, 这样一趟过去后,最大或最小的数字被交换到了最后一位, 然后再从头開始进行两两比較交换,直到倒 ...

  9. 7.使用php描述冒泡排序,PHP 数据结构 算法描述 冒泡排序 bubble sort

    PHP 数据结构 算法描述 冒泡排序 bubble sort 复制代码 代码如下: /** * 冒泡排序 bubble sort * * 原理:多次循环进行比较,每次比较时将最大数移动到最上面.每次循 ...

最新文章

  1. Node.js v0.10版本发布
  2. 【青少年编程竞赛交流】11月份微信图文索引
  3. Jupyter 环境配置
  4. 【指标统计】MsgProxy消息代理配置
  5. windows下训练FCN模型所遇到的问题
  6. WCF系列(五) -- 也谈序列化(下)
  7. DeepLearning.AI第一部分第三周、 浅层神经网络(Shallow neural networks)
  8. [轉]Flash/Flex监听浏览器的关闭事件
  9. [BEC][hujiang] Lesson02 Unit1:Working life ---Reading
  10. [leetcode双周赛]5312. 大小为 K 且平均值大于等于阈值的子数组数目
  11. python提供的内置函数有哪些_python内置函数介绍
  12. FCKeditor在ASP.NET环境中配置使用
  13. Redis由分布式锁造成的重大事故
  14. c语言程序设计精髓第七周编程题,C语言程序设计精髓编程题在线测试
  15. 计算机二级--java篇
  16. Astar算法基本概念及其实现
  17. Unity Animator Animation详解及应用实例教程
  18. 云笔记+心情日记类APP推荐
  19. vue-3d-model vue 实现3D 图像显示
  20. SQL 2008函数大全

热门文章

  1. C#编程(五十三)----------字典DictionaryTKey,TValue
  2. 《Adobe Illustrator CS6中文版经典教程(彩色版)》—第0课0.11节创建与应用图案...
  3. CentOS7关闭防火墙方法
  4. Codeforces Round #324 (Div. 2) E. Anton and Ira 贪心
  5. ExecutorService线程池
  6. RHEL5系统配置双网卡绑定
  7. php对象的底层机制
  8. C#基础系列问题一break、continue、return、goto语句
  9. xp 5gt ***
  10. .net和java互操作