您的位置:首页 > 其它

poj 3761 Bubble Sort

2012-08-13 16:19 337 查看
问题描述如下:

一个具有n个元素的排列,经过k趟bubble sort排到有序状态。求出满足此条件的排列个数。

首先,了解反序表的概念。《计算机程序设计艺术》第三卷5.1.1反序部分有说明。简述如下:a1,a2,...,an是集合{1,2,...,n}的一个排列。令bj为位于j左边但是大于j的元素个数,就能得到排列a1,a2,...,an的反序表b1,b2,...,b3。比如说:排列

5 9 1 8 2 6 4 7 3

有反序表

位置: 1 2 3 4 5 6 7 8 9

b 2 3 6 4 0 2 2 1 0

不管什么样子的排列,只要可以比较大小,就可以映射到集合{1,2,...,n}上的一个排列。不难发现,每一趟bubble sort ,都会将这一趟之前反序数大于0的元素的反序数减1。经过k趟之后,所有元素的反序数均为0,也就是说,反序表为n个0,亦即有序状态。因此,题目可以演化为,n个元素的排列,反序表中最大值为k,求这样的排列有多少种。

易知,第i个元素的反序数取值范围是[0,n-i],还注意到一个事实,就是:每个元素的反序数取值是相互独立。为什么呢?事实上我开始以为是不独立的,但是验证了几个例子以后,我推翻自己的想法。当然这只是直觉上的。但是可以从《计算机程序设计艺术》第三卷反序部分得到一点理论支撑。引用书上的话“一张反序表唯一的确定一个排列”(我没有细看这个事实的证明)。因此,反序表和排列之间一一对应。全排列个数是n!,因此反序表的个数也是n!,这只有在取值相互独立的情况下得到。

回到题目,反序数最大为k。那么当n-i<=k,即i>=n-k的时候,元素i可随意放。因为不管怎么放,他们的反序数都不会大于k,取值的个数由i来决定。当i<n-k时候,这些元素的反序数只可以在[0,k]之间选择。这样,我把n个元素分为两类来讨论,前面一类元素有k+1(即i = 1~k+1)个,后面一类有n-k-1(即i=n-k-1~n)个元素。先考虑前面一种情况,我们对前面k+1个元素进行全排列,他们的反序数一定在0~k之间,那么就有(k+1)!种排序的方法。后面一类,我们可以与前面的元素进行对调,但是他们的值之差不能超过k(我们要保证他们的反序数在0~k之间)那么有(k+1)种选择去对调,因此有(k+1)^(n-k-1)种方案。由乘法定理,易得k!*(k+1)^(n-k)。

但是,这不是最终答案。上面的结果,只是说经过不超过k趟排到有序状态。并不符合题意。因此,必须保证有一个元素的反序数为k。最好的做法就是计算出不超过k-1趟就排到有序状态的排列数,减掉。很容易利用刚才的结论,得到不超过k-1趟就排到有序状态的排列数为:(k-1)!*(k)^(n-k+1)。两者想减,即得到结果:

k!*((k + 1)^(n - k) - k ^(n - k))。

View Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#include<cstring>
#include<vector>
#include<string>
#define LL long long
#define MOD 20100713
using namespace std;
LL A[1000024];
void Init(  )
{
A[0] = 1LL;
for( LL i = 1 ; i <= 1000000LL; i++ )
A[i] = ( i * A[i-1] )%MOD;
}
LL Pow( LL n,LL a )
{
LL ans = 1;
while( n )
{
if( n&1 ) ans = (ans*a)%MOD;
a = (a*a)%MOD;
n >>= 1;
}
return ans;
}
int main(  )
{
int T;
LL n,k;
Init();
while( scanf( "%d",&T )==1 )
{
while( T-- )
{
scanf( "%I64d %I64d",&n,&k );
LL ans = (Pow( n-k , k+1 ) - Pow( n - k , k )+MOD)%MOD;
printf( "%I64d\n",(ans*A[k])%MOD );
}
}
//system( "pause" );
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: