您的位置:首页 > 大数据 > 人工智能

[UVA11076]Add Again

2015-10-09 10:39 399 查看
题目链接: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以内的阶乘,可以先跑一遍阶乘再把数据直接存入一个数组里备用。

代码如下:

#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <climits>
#include <complex>
#include <fstream>
#include <cassert>
#include <cstdio>
#include <bitset>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <ctime>
#include <set>
#include <map>
#include <cmath>

using namespace std;

typedef long long LL;
const int maxn = 13;
LL po[maxn] = {1,1,2,6,24,120,720,5040,40320,362880,3628800,39916800,479001600};
int but[10];
int n;
int num[maxn];

inline bool scan_d(int &num) {
char in;
bool ok = false;
in = getchar();
if(in == EOF) {
return false;
}
while(in != '-' && (in < '0' || in > '9')) {
in = getchar();
}
if(in == '-') {
ok = true;
num = 0;
}
else {
num = in - '0';
}
while(in = getchar(), in >= '0' && in <= '9') {
num *= 10;
num += in - '0';
}
if(ok) {
num = -num;
}
return true;
}

int main() {
while(~scan_d(n) && n) {
int flag = 0;
LL sum = 0;
memset(but, 0, sizeof(but));
for(int i = 0; i < n; i++) {
scan_d(num[i]);
sum += num[i];
but[num[i]]++;
}
LL poi = po
;
for(int i = 0; i <= 9; i++) {
if(but[i] == n) {
flag = 1;
break;
}
if(but[i] != 0) {
poi /= po[but[i]];
}
}
if(flag) {
for(int i = 0; i < n; i++) {
printf("%d", num[0]);
if(num[0] == 0) {
break;
}
}
printf("\n");
}
else {
LL ans = 0;
sum = sum * poi / n;
for(int i = 0; i < n; i++) {
ans += sum;
sum *= 10;
}
printf("%llu\n", ans);
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: