zoukankan      html  css  js  c++  java
  • [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 }
  • 相关阅读:
    coding
    我的大学
    《活出生命的意义》读后感
    《世界是数字的》读后感
    学习进度表
    阅读《我是一只IT小小鸟有感》
    《软件工程师职业规范》心得
    软件工程第二次结对作业
    软件工程第三次作业
    软件工程第二次作业
  • 原文地址:https://www.cnblogs.com/kirai/p/4863216.html
Copyright © 2011-2022 走看看