题意:
给定
k
k
k 种不同的字符,第
i
i
i 种字符有
c
i
c_i
ci 种。
所有字符可以随意顺序放,得到的序列按照相同数分成一段,问最后从前到后的每段数量是否可以构成一个斐波那契数列。
如:
a
:
1
,
b
:
2
,
c
:
3
a:1,b:2,c:3
a:1,b:2,c:3
a
b
c
b
c
c
abcbcc
abcbcc 对应为:
1
(
a
)
,
1
(
b
)
,
1
(
c
)
,
1
(
b
)
,
2
(
c
c
)
1(a),1(b),1(c),1(b),2(cc)
1(a),1(b),1(c),1(b),2(cc)
a
b
b
c
c
c
abbccc
abbccc 对应为:
1
(
a
)
,
2
(
b
b
)
,
3
(
c
c
c
)
1(a),2(bb),3(ccc)
1(a),2(bb),3(ccc)
如:
a
:
1
,
b
:
4
,
c
:
2
a:1,b:4,c:2
a:1,b:4,c:2
a
b
c
c
b
b
b
abccbbb
abccbbb 对应为:
1
(
a
)
,
1
(
b
)
,
2
(
c
)
,
3
(
b
b
b
)
1(a),1(b),2(c),3(bbb)
1(a),1(b),2(c),3(bbb),这就是一个斐波那契数列
注意第一个和第二个值都为
1
1
1
数据范围: 1 ≤ k ≤ 100 , 1 ≤ c i ≤ 1 0 9 1\leq k\leq 100, 1\leq c_i\leq 10^9 1≤k≤100,1≤ci≤109
题解:
首先,易得斐波那契数列的个数是不会很多的,因为每个数最多
1
0
9
10^9
109,所以最大的一项不能超过
1
0
9
10^9
109;其次,因为需要将所有字符都分配出去,因此对应的字符数量和一定是一个斐波那契数列的前缀和。不满足上述的条件必然不能构成。
之后我们考虑如何去构造这个序列。
从最大的数开始,首先对应的字符种类的字符数量必须不小于这个最大的数,那么如果有多个,该选择哪个数呢?
对于最大的数,如果其不选择 f [ i ] f[i] f[i],那么其最多可以选择的和是 f [ i − 1 ] + f [ i − 3 ] + f [ i − 5 ] + . . . f[i-1]+f[i-3]+f[i-5]+... f[i−1]+f[i−3]+f[i−5]+...
我们考虑如下问题:
f
[
i
]
≥
f
[
i
−
1
]
+
f
[
i
−
3
]
+
f
[
i
−
5
]
+
.
.
.
f[i]\geq f[i-1]+f[i-3]+f[i-5]+...
f[i]≥f[i−1]+f[i−3]+f[i−5]+...,这里斐波拉契下标从
0
0
0 开始
这个问题很好证明:
综上,如果最大的数 a m a x a_{max} amax 不选择 f [ i ] f[i] f[i],那么他最多只能选择的数不会大于 f [ i ] f[i] f[i],甚至小于 f [ i ] f[i] f[i],那么对于这个最大的数可能会用不完其所有的字符。因此,贪心地每次让最大的数选择即可,如果当前 f [ i ] f[i] f[i] 找不到一个可以覆盖它的字符种类,那么无解。
代码:
#include
using namespace std;
typedef long long ll;
const int N = 110;
map<ll, int> mp;
ll fib[N];
ll a[N], n;
void solve() {
scanf("%lld", &n);
ll sum = 0;
for (int i = 1; i <= n; ++i)scanf("%lld", &a[i]), sum += a[i];
if (!mp.count(sum)) {
puts("NO");
return ;
}
int last = 0;
int st = mp[sum];
for (int i = st; i >= 0; --i) {
int id = -1;
for (int j = 1; j <= n; ++j)
if (j != last) if (id == -1 || a[id] < a[j]) id = j;
if (id == -1 || a[id] < fib[i]) {
puts("NO");
return ;
}
a[id] -= fib[i];
last = id;
}
puts("YES");
}
int main()
{
fib[0] = fib[1] = 1;
mp[1] = 0, mp[2] = 1;
ll sum = 2;
for (int i = 2; i <= 43; ++i) {
fib[i] = fib[i - 1] + fib[i - 2];
sum += fib[i];
mp[sum] = i;
}
int T = 1;
scanf("%d", &T);
for (int i = 1; i <= T; ++i) {
solve();
}
return 0;
}