本来以为还是签完到就开始自闭,没想到今天的邀请赛还摸出一道C。这题看提交数其实不多,而且题目表达也挺复杂。简单来说,就是把一个数W拆成若干个Fib(Fib(x))之和,其中x各不相同。题目数据是1 \leq W \leq 10^{100000},乍一看好像很夸张。其实去算Fib \left( x \right) \leq 10^{100000}就会发现,其实也就x \leq 317811,再算Fib \left( x \right) \leq 317811,发现其实也就28个数。不过这28个数真的挺长的,打表还真打不下,好在Fib现算一遍也就O(logN)。
然后拆数我写的就很low了,由于字典序最小,我就用了O\left( n^2 \right)的朴素方法。其实只有2和5需要处理,但是第一次交的时候好像我其它地方漏了个条件,于是WA了。后来保险就用了,总复杂度是O\left( 28^2 T + \log N \right)。
import java.math.BigInteger;
import java.util.Scanner;
public class Main {
// 打表,前28项
final static int[] ind = {0,1,2,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811};
static boolean[] inS = new boolean[30];
static boolean[] used = new boolean[30];
static BigInteger[] ff = new BigInteger[30];
static int sCnt;
static boolean check(BigInteger W, int st) {
int curCheck = st;
sCnt = 0;
for (int i = 1; i < ind.length; i++) used[i] = false;
while (W.compareTo(BigInteger.ZERO) > 0 && curCheck > 0) {
if (!inS[curCheck] && W.compareTo(ff[curCheck]) >= 0) {
W = W.subtract(ff[curCheck]);
used[curCheck] = true;
sCnt++;
}
curCheck--;
}
if (sCnt > 0 && W.equals(BigInteger.ZERO)) {
for (int i = 1; i < ind.length; i++) inS[i] = inS[i] || used[i];
return true;
}
return false;
}
public static void main(String[] args) {
int curInd, cnt;
BigInteger fn_2, fn_1, fn;
fn_2 = BigInteger.ONE;
fn_1 = BigInteger.ONE;
ff[1] = BigInteger.ONE; ff[2] = BigInteger.ONE; ff[3] = BigInteger.ONE;
cnt = 2; curInd = 4;
while (curInd < ind.length) {
fn = fn_2.add(fn_1);
fn_2 = fn_1;
fn_1 = fn;
cnt++;
if (cnt == ind[curInd]) {
ff[curInd] = fn;
curInd++;
}
}
// 读入
Scanner sc = new Scanner(System.in);
int T, curCheck;
BigInteger W;
T = sc.nextInt();
while (T-- > 0) {
W = sc.nextBigInteger();
sCnt = 0; // 集合S大小
// 初始化inS
for (int i = 1; i < ind.length; i++) inS[i] = false;
// 检查
if (!check(W, ind.length - 1)) {
// 失败
System.out.println("-1");
continue;
}
// 对每个数,向前分割
BigInteger num;
curCheck = ind.length - 1;
while (curCheck > 0) {
while (curCheck > 0 && !inS[curCheck]) curCheck--;
if (!inS[curCheck]) break;
// inS[curCheck] = true
if (check(ff[curCheck], curCheck - 1)) {
inS[curCheck] = false;
}
curCheck--;
}
// 重新计算sCnt
sCnt = 0;
for (int i = 1; i < ind.length; i++) if (inS[i]) sCnt += 1;
// 打印
for (int i = 1; i < ind.length; i++) {
if (inS[i]) {
System.out.print(i);
sCnt--;
if (sCnt > 0) System.out.print(' ');
}
}
System.out.println();
}
}
}