[UVA11076]Add Again

题目链接: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 }
原文地址:https://www.cnblogs.com/kirai/p/4863216.html