Description
给定一个长度为 n n n 的序列 a a a,求它的最长斐波那契子序列。空间限制 64MB。
1 ≤ n ≤ 3000 , 1 ≤ ∣ a i ∣ ≤ 1 0 9 1 \leq n \leq 3000, 1 \leq |a_i| \leq 10^9 1≤n≤3000,1≤∣ai∣≤109。
Solution
可以发现确定了子序列的前两项,那么后面的可以推出来。所以令 f i , j ( i < j ) f_{i,j} \quad (i<j) fi,j(i<j) 为前两项为 a i a_i ai 和 a j a_j aj 的最长斐波那契子序列的长度 − 2 -2 −2。转移时找一个满足 a k = a i + a j ∧ k > j a_k = a_i + a_j \land k > j ak=ai+aj∧k>j 的最小的下标 k k k,那么 f i , j = f j , k + 1 f_{i,j} = f_{j,k} + 1 fi,j=fj,k+1。 那么找到 f i , j f_{i,j} fi,j 最大的 ( a i , a j ) (a_i,a_j) (ai,aj) 将最长斐波那契子序列推出来即可。
考虑如何求
k
k
k。我会暴力! 可以搞个序列自动机,
n
x
t
i
,
j
nxt_{i,j}
nxti,j 为下标
i
i
i 之后第一个
j
j
j 的下标,不包括
i
i
i。可是
1
≤
∣
a
i
∣
≤
1
0
9
1 \leq |a_i| \leq 10^9
1≤∣ai∣≤109,我会离散化! 喂喂,离散化是不能保证两数之和等于另一个数的。我会 map! ,看我的绝招 unordered_map。
于是写了一发 MLE on test 16。原来是序列自动机超空间了,没关系倒着 dp 不需要
i
i
i 这一维,但是要把 unordered_map 清空。再来——MLE on test 16。没关系,我把 int 换成 short。一定能过了——TLE on test 16。读写优化,卡常!还是 TLE on test 16?唉,好像可以变换
i
i
i 和
j
j
j 的枚举顺序,这样每次不用清空 unordered_map 了。好的奈斯,过第十六个 … TLE on 27 开玩笑吧?看来老虎不发威你当我是病猫啊 手写 HashTable 散列表,终于过啦。
Solution
#include <bits/stdc++.h>
using namespace std;
const int N = 3000 + 1;
inline int read() {
int x = 0, f = 0; char ch = 0;
while (!isdigit(ch)) f |= ch == '-', ch = getchar();
while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
return f ? -x : x;
}
inline void write(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
int n, a[N];
short f[N][N];
int mxl = -1, ansa, ansb;
struct HashTable {
static const int p = 999997;
int head[p], sz;
struct Node {
int nxt, val, key;
}data[N];
HashTable() {
memset(head, -1, sizeof(head));
sz = 0;
}
int get(int key) {
return (key % p + p) % p;
}
void insert(int key, int val) {
data[++sz] = (Node){head[get(key)], val, key};
head[get(key)] = sz;
}
int find(int key) {
for (int i = head[get(key)]; i != -1; i = data[i].nxt)
if (data[i].key == key) return data[i].val;
return -1;
}
} pos;
int main() {
freopen("fibsubseq.in", "r", stdin);
freopen("fibsubseq.out", "w", stdout);
n = read();
for (register short i = 1; i <= n; ++i) a[i] = read();
if (n == 1) {
printf("1\n%d\n", a[1]); return 0;
}
for (register short i = n; i >= 1; --i) {
for (register short j = i - 1; j >= 1; --j) {
short k = pos.find(a[i] + a[j]);
if (k != -1) f[j][i] = f[i][k] + 1;
if (f[j][i] > mxl) {
mxl = f[j][i];
ansa = a[j], ansb = a[i];
}
}
pos.insert(a[i], i);
}
write(mxl + 2); puts("");
write(ansa); putchar(' '); write(ansb); putchar(' ');
while(mxl--) {
write(ansa + ansb); putchar(' ');
int t = ansa;
ansa = ansb, ansb += t;
}
return 0;
}