Viaky
Hope,is there.

4.膜拜大犇

(communicate.pas/c/cpp)

【题目背景】

ZSZ终于登上了传说中的大犇星的土地并且见到了神秘的大犇们。由于大犇的智商过高,为了交流方便和简洁, 大犇们在大犇星上使用自己的语言。悲剧的是,大犇们由于长期远离人类生活,早已忘了人类的语言。That is to say,ZSZ和大犇都无法理解对方的语言,但是ZSZ发明了一种用数字交流的方法。这种交流方法是这样的,首先,大犇把一个非常大的数字告诉ZSZ,ZSZ破解这个数字的含义后,再把一个很小的数字加到这个大数上面,把结果告诉大犇,作为ZSZ对大犇的膜拜。

【题目描述】

大犇用一种非常简单的方式来表示数字——掰手指。大犇只有一只手,但这只手上有成千上万的手指,这些手指排成一列,分别编号为1,2,3……。大犇的任意两根手指都能随意交换位置,他们就是通过这方法计数的。

一个大犇用一个人类的手演示了如何用手指计数。如果把五根手指——拇指、食指、中指、无名指和小指分别编号为1,2,3,4和5,当它们按正常顺序排列时,形成了5位数12345,当你交换无名指和小指的位置时,会形成5位数12354,当你把五个手指的顺序完全颠倒时,会形成54321,在所有能够形成的120个5位数中,12345最小,它表示1;12354第二小,它表示2;54321最大,它表示120。下表展示了只有3根手指时能够形成的6个3位数和它们代表的数字:

三进制数

123

132

213

231

312

321

代表的数字

1

2

3

4

5

6

悲剧的是,由于ZSZ对数字过敏,见到数字就头晕…于是他想到了你(别告诉我你也晕啊)。现在你有幸成为了第一个和大犇交流的地球人。一个大犇会让你看他的手指,ZSZ会告诉你要加上去的很小的数。你的任务是,把大犇用手指表示的数与ZSZ告诉你的数相加,并根据相加的结果改变大犇手指的排列顺序。输入数据保证这个结果不会超出大犇手指能表示的范围。

【输入格式】

输入文件communicate.in包括三行,第一行有一个正整数N,表示大犇手指的数目(1 <= N <= 10000)。第二行是一个正整数M,表示要加上去的小整数(1 <= M <= 100)。下一行是1到N这N个整数的一个排列,用空格隔开,表示大犇手指的排列顺序。

【输出格式】

输出文件communicate.out只有一行,这一行含有N个整数,表示改变后的大犇手指的排列顺序。每两个相邻的数中间用一个空格分开,不能有多余的空格。

【样例输入】

5

3

1 2 3 4 5

【样例输出】

1 2 4 5 3

【数据规模】

对于30%的数据,N<=15;

对于60%的数据,N<=50;

对于全部的数据,N<=10000;

康托展开的公式

  把一个整数X展开成如下形式:

  X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[2]*1!+a[1]*0!

  其中,a为整数,并且0<=a[i]<i(1<=i<=n)

康托展开的应用实例

  {1,2,3,4,...,n}表示1,2,3,...,n的排列如 {1,2,3} 按从小到大排列一共6个。123 132 213 231 312 321 。

  代表的数字 1 2 3 4 5 6 也就是把10进制数与一个排列对应起来。

  他们间的对应关系可由康托展开来找到。

举个例子:1324是{1,2,3,4}排列数中第几个大的数:第一位是1小于1的数没有,是0个 0*3! 第二位是3小于3的数有1和2,但1已经在第一位了,所以只有一个数2 1*2! 。第三位是2小于2的数是1,但1在第一位,所以有0个数 0*1! ,所以比1324小的排列有0*3!+1*2!+0*1!=2个,1324是第三个大数。

 

康托展开的逆运算

  例 {1,2,3,4,5}的全排列,并且已经从小到大排序完毕

  (1)找出第96个数

  首先用96-1得到95

  用95去除4! 得到3余23

  用23去除3! 得到3余5

  用5去除2!得到2余1

  用1去除1!得到1余0有3个数比它小的数是4

  所以第一位是4

  有3个数比它小的数是4但4已经在之前出现过了所以是5(因为4在之前出现过了所以实际比5小的数是3个)

  有2个数比它小的数是3

  有1个数比它小的数是2

  最后一个数只能是1

  所以这个数是45321

  (2)找出第16个数

  首先用16-1得到15

  用15去除4!得到0余15

  用15去除3!得到2余3

  用3去除2!得到1余1

  用1去除1!得到1余0

  有0个数比它小的数是1

  有2个数比它小的数是3 但由于1已经在之前出现过了所以是4(因为1在之前出现过了所以实际比4小的数是2)

  有1个数比它小的数是2 但由于1已经在之前出现过了所以是3(因为1在之前出现过了所以实际比3小的数是1)

  有1个数比它小得数是2 但由于1,3,4已经在之前出现过了所以是5(因为1,3,4在之前出现过了所以实际比5小的数是1)

  最后一个数只能是2

  所以这个数是14352

1 program ktzk;
2
3 var
4
5 n,m,i,num,j:longint;
6
7 ar:array[1..10001]of integer;
8
9 hash:array[1..10001]of boolean;
10
11 fac:array[0..10001]of int64;
12
13 sum,t:int64;
14
15 begin
16
17 fillchar(hash,sizeof(hash),false);
18
19 read(n,m);
20
21 num:=0; sum:=0;
22
23 for i:=1 to n do//计算阶乘
24
25 begin
26
27 fac[i]:=1;
28
29 for j:=1 to i do
30
31 fac[i]:=fac[i]*j;
32
33 end;
34
35 for i:=1 to n do
36
37 begin
38
39 read(ar[i]);
40
41 num:=0;
42
43 hash[ar[i]]:=true;//记录已经出现过a[i]
44
45 for j:=(ar[i]-1)downto 1 do//循环比a[i]小的正整数
46
47 if not hash[j] then inc(num);//如果没出现过就计数。
48
49 sum:=sum+num*fac[n-i];//公式
50
51 end;
52
53 inc(sum,m+1);
54
55 //以下为逆运算
56
57 dec(sum);
58
59 fillchar(hash,sizeof(hash),false);
60
61 for i:=n-1 downto 1 do
62
63 begin
64
65 j:=0;
66
67 t:=sum div fac[i];
68
69 sum:=sum mod fac[i];
70
71 while t>=0 do
72
73 begin
74
75 inc(j);
76
77 if not hash[j] then dec(t);
78
79 end;
80
81 hash[j]:=true;
82
83 write(j,' ');
84
85 end;
86
87 for i:=1 to n do if not hash[i] then write(i,' ');
88
89 end.
posted on 2011-03-16 15:34  Viaky  阅读(329)  评论(0编辑  收藏  举报