题目链接:http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=33478
题意:由0~9中n个数字(可重复)组成的数组,把每个全排列看成一个n位数,求所有全排列的和。
GF的方法:http://www.cnblogs.com/helenawang/p/4862782.html
总感觉和她的有点儿不一样,嗯……跑了一下她的程序,23ms。为了证明我们家我说了算,给出如下更快的算法(6ms)
高中学过的姿势,如果一个排列中有重复元素出现的话,那么它的排列数会按照一个规律减少:
首先给出一个序列 a1 a2 a3 .... an
我们把它的全排列写出来,发现它们每一位的数字出现的次数都是相同的,而且要求的和就是“重复出现次数*∑(1,n)ai”。可是不同的排列,这些数字出现的次数是不一样的,我想知道如何求出这个出现的次数。于是有了如下分析:
1.如果这n个数没有任何重复,那么它的排列数为: n*(n-1)*(n-2)...1 = A(n,n) = n!,这是随意摆放的情况。
2.如果这n个数有一组数重复了x次,那么按照高中老师讲过的排列组合,要相应去除掉对应个数的排列数。那么它的排列数为:A(n,n)/A(x,x),怎么样,是不是很眼熟?
接下来推广到m组数据,分别重复x1 x2 .... xm次,我们很容易地得出一个结论,就是把这个排列数细分拆成如下形式:
假设n个数字,分别重复了x1 x2 .....xn次(xi >= 1),那么这个排列的个数为:poi = A(n,n) / (A(x1,x1)*A(x2,x2)...A(xn,xn))
因为数字是在[0,9]这个区间的,我们可以用一个桶来统计每一位出现的次数。这样,对于这个排列,每一位出现的次数也就统计了下来并且计算出poi。
仔细观察可以知道,重复出现次数 = poi / n,这样,某一位的总和就能求出来了。接下来把所有位的和都求出来,那么模拟一下乘法搞一下就AC了。
输入数据可以用getchar优化一下scanf,俗称输入挂。(它辅助我从19ms杀到了6ms)
因为n<=12,用到了12以内的阶乘,可以先跑一遍阶乘再把数据直接存入一个数组里备用。
代码如下:
1 #include <algorithm> 2 #include <iostream> 3 #include <iomanip> 4 #include <cstring> 5 #include <climits> 6 #include <complex> 7 #include <fstream> 8 #include <cassert> 9 #include <cstdio> 10 #include <bitset> 11 #include <vector> 12 #include <deque> 13 #include <queue> 14 #include <stack> 15 #include <ctime> 16 #include <set> 17 #include <map> 18 #include <cmath> 19 20 using namespace std; 21 22 typedef long long LL; 23 const int maxn = 13; 24 LL po[maxn] = {1,1,2,6,24,120,720,5040,40320,362880,3628800,39916800,479001600}; 25 int but[10]; 26 int n; 27 int num[maxn]; 28 29 inline bool scan_d(int &num) { 30 char in; 31 bool ok = false; 32 in = getchar(); 33 if(in == EOF) { 34 return false; 35 } 36 while(in != '-' && (in < '0' || in > '9')) { 37 in = getchar(); 38 } 39 if(in == '-') { 40 ok = true; 41 num = 0; 42 } 43 else { 44 num = in - '0'; 45 } 46 while(in = getchar(), in >= '0' && in <= '9') { 47 num *= 10; 48 num += in - '0'; 49 } 50 if(ok) { 51 num = -num; 52 } 53 return true; 54 } 55 56 int main() { 57 while(~scan_d(n) && n) { 58 int flag = 0; 59 LL sum = 0; 60 memset(but, 0, sizeof(but)); 61 for(int i = 0; i < n; i++) { 62 scan_d(num[i]); 63 sum += num[i]; 64 but[num[i]]++; 65 } 66 LL poi = po[n]; 67 for(int i = 0; i <= 9; i++) { 68 if(but[i] == n) { 69 flag = 1; 70 break; 71 } 72 if(but[i] != 0) { 73 poi /= po[but[i]]; 74 } 75 } 76 if(flag) { 77 for(int i = 0; i < n; i++) { 78 printf("%d", num[0]); 79 if(num[0] == 0) { 80 break; 81 } 82 } 83 printf(" "); 84 } 85 else { 86 LL ans = 0; 87 sum = sum * poi / n; 88 for(int i = 0; i < n; i++) { 89 ans += sum; 90 sum *= 10; 91 } 92 printf("%llu ", ans); 93 } 94 } 95 return 0; 96 }