给你10个数 ai ,表示 1 到某个数 N 中出现的 0 到 9 的个数,现在要求输出这个数 N ,如果无解则输出 −1
ai≤10512 保证输入的是非负整数并且至少有一个为正整数。
一点特别关键的是,题目给了我们 1 ~ N 中 0 ~ 9 出现的次数,那么就相当于告诉了我们 1 ~ N 所有数的位数和。而我们又不难发现 1 ~ 9 的贡献是 1∗9 , 10 ~ 99 的贡献是 2∗90 , 100 ~ 999 的贡献是 3∗900 …… 显然 i 位数的贡献是 i∗9∗10i−1 。
有了这个我们可以依次判断 N 是不是 i 位数。简单来说就是把 i 从1枚举,如果当前的位数和比第 i 位数的贡献大,就用把总位数减去这个贡献, i 加一。否则我们要找的 N 就是 i 位数。知道位数后,就可以很简单的求出 N 的具体数字了,就是直接用剩下的位数除以 i ,假设商为 j ,如果除不尽就肯定无解,处的尽就是 i 位数的第 j 个数。
但是,这样判断不一定正确,应为我们只是用总位数去判断,并没有单独考虑某一个数字是否合法,有可能一个数字多了一,一个少了一,我们依旧会得出答案。这种问题也很好解决,我们只需算出 1 ~ N 中 1 ~ 9 出现的次数,判断一下跟读入进来的一不一样就可以了。
判断时我们只需把 N 从地位到高位枚举,来更新答案,假设现在做到第 i 位,我们要求 N 的后 i 有多少个 1 (其他数字依此类推)。设我们要求的是 Fi ,那么 Fi=(Ni−1)∗Gi+Fi−1+Pi 。
Hi :表示 N 后 i 位组成的数。
Ni :表示 N 的第 i 位是什么数字。
Gi :表示 10i 内某一个数字出现的次数( 1 ~ 9 出现次数一样)。
Pi :分三种情况:如果 Ni 大于 1 则 Pi=10i−1 ;如果 Ni 等于 1 则 Pi=Hi−1+1 ;如果 Ni 小于 1 则 Pi=0 。
其中 (Ni−1)∗Gi 表示 1 ~ (Ni−1)∗10i−1 中前 i−1 位中 1 的个数,而 Fi−1 表示 (Ni−1)∗10i−1 ~ Hi 中前 i−1 位中 1 的次数。 Pi 表示 1 ~ Hi 中第 i 位中 1 的个数。推理过程比较简单就不细说了。
最后看到输入的 ai 那么大,那就要做好套高精度的准备。
由于除了高精度外实现比较简单,所以是一个高精度的模板题!
高精度模板(加,减,乘,除,比较)
//GDOI2012 digit YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 600;
struct Node {int Num[MAXN], Bit;};
char S[MAXN];
Node All, Ans, Ten, One, Dig[10], Real[10];
int Judge(Node A, Node B) {
if (A.Bit > B.Bit) return 1;
if (A.Bit < B.Bit) return 0;
for (int i = A.Bit; i; i --) {
if (A.Num[i] > B.Num[i]) return 1;
if (A.Num[i] < B.Num[i]) return 0;
}
return 2;
}
Node Dec(Node A, Node B) {
Node C;
memset(C.Num, 0, sizeof C.Num);
for (int i = 1; i <= A.Bit; i ++) {
C.Num[i] = C.Num[i] + A.Num[i] - B.Num[i];
if (C.Num[i] < 0) C.Num[i] += 10, C.Num[i + 1] --;
}
C.Bit = A.Bit;
while (!C.Num[C.Bit]) C.Bit --;
return C;
}
Node Add(Node A, Node B) {
Node C;
memset(C.Num, 0, sizeof C.Num);
C.Bit = max(A.Bit, B.Bit);
for (int i = 1; i <= C.Bit; i ++) {
C.Num[i] = C.Num[i] + A.Num[i] + B.Num[i];
C.Num[i + 1] = C.Num[i] / 10;
C.Num[i] = C.Num[i] % 10;
}
if (C.Num[C.Bit + 1]) C.Bit ++;
return C;
}
Node Mul(Node A, Node B) {
Node C;
memset(C.Num, 0, sizeof C.Num);
for (int i = 1; i <= A.Bit; i ++)
for (int j = 1; j <= B.Bit; j ++) {
C.Num[i + j - 1] = C.Num[i + j - 1] + (A.Num[i] * B.Num[j]);
C.Num[i + j] = C.Num[i + j] + (C.Num[i + j - 1] / 10);
C.Num[i + j - 1] = C.Num[i + j - 1] % 10;
}
C.Bit = A.Bit + B.Bit - 1;
if (C.Num[C.Bit + 1]) C.Bit ++;
return C;
}
Node Div(Node A, Node B) {
Node C, Tmp;
memset(C.Num, 0, sizeof C.Num);
memset(Tmp.Num, 0, sizeof Tmp.Num);
Tmp.Bit = A.Bit - B.Bit + 1, Tmp.Num[Tmp.Bit] = 1;
C.Bit = 0;
for (; Tmp.Bit; ) {
Node D = Mul(B, Tmp);
while (Judge(A, D)) {
A = Dec(A, D);
C = Add(C, Tmp);
}
Tmp.Num[Tmp.Bit] = 0;
Tmp.Num[-- Tmp.Bit] = 1;
}
return C;
}
Node Check_Bit() {
Node Tmp, Bit;
memset(Bit.Num, 0, sizeof Bit.Num), memset(Tmp.Num, 0, sizeof Tmp.Num);
Tmp.Bit = 1, Tmp.Num[1] = 9;
Bit.Bit = 0, Bit.Num[1] = 0;
while (All.Bit > 0) {
Bit = Add(Bit, One);
Node Check = Mul(Tmp, Bit);
if (!Judge(All, Check)) break;
All = Dec(All, Check);
Tmp = Mul(Tmp, Ten);
}
return Bit;
}
int TransInt(Node Bit) {
int Ret = 0;
for (int i = Bit.Bit; i; i --) Ret = Ret * 10 + Bit.Num[i];
return Ret;
}
void GetAns() {
for (int i = 0; i < 10; i ++) All = Add(All, Dig[i]);
Node Bit = Check_Bit();
Node Inc = Div(All, Bit);
int bit = TransInt(Bit);
Ans = One;
for (int i = 1; i < bit; i ++) Ans = Mul(Ans, Ten);
if (Inc.Bit != 0) Ans = Add(Ans, Dec(Inc, One));
}
bool CheckAns() {
Node Tmp, Cnt, S, All;
memset(Tmp.Num, 0, sizeof Tmp.Num), memset(All.Num, 0, sizeof All.Num);
memset(Cnt.Num, 0, sizeof Cnt.Num), memset(S.Num, 0, sizeof S.Num);
All.Bit = 1, All.Num[1] = 1;
Tmp.Bit = 1, Tmp.Num[1] = 1;
Cnt.Bit = 1, Cnt.Num[1] = 1;
S.Bit = 0;
for (int i = 1; i <= Ans.Num[1]; i ++) Real[i].Bit = 1, Real[i].Num[1] = 1;
for (int i = 2; i <= Ans.Bit; i ++) {
Cnt = Mul(Cnt, Ten);
S.Num[++ S.Bit] = Ans.Num[i - 1];
Node T;
memset(T.Num, 0, sizeof T.Num);
T.Bit = 1; T.Num[1] = Ans.Num[i];
for (int j = 1; j < 10; j ++) {
Real[j] = Add(Real[j], Mul(T, All));
if (j < Ans.Num[i]) Real[j] = Add(Cnt, Real[j]);
if (j == Ans.Num[i]) Real[j] = Add(Add(S, One), Real[j]);
}
Tmp = Add(Cnt, Mul(All, Dec(Ten, One)));
All = Add(All, Tmp);
}
for (int i = 1; i < 10; i ++)
if (Judge(Dig[i], Real[i]) < 2) return 0;
return 1;
}
int main() {
One.Bit = 1, One.Num[1] = 1;
Ten.Bit = 2, Ten.Num[1] = 0, Ten.Num[2] = 1;
for (int i = 0; i < 10; i ++) {
scanf("%s", S + 1);
int Len = strlen(S + 1);
for (int j = 1; j <= Len; j ++) Dig[i].Num[j] = S[Len - j + 1] - '0';
Dig[i].Bit = Len;
}
GetAns();
bool Check = CheckAns();
if (!Check) {
printf("-1\n");
return 0;
}
for (int i = Ans.Bit; i; i --) printf("%d", Ans.Num[i]);
printf("\n");
}