题解:P6361 [CEOI 2018] Fibonacci representations
某神秘模拟赛 T4,将凶区冈创飞。
手玩一下,发现如果将
对于一个正整数,我们可以将其拆成 Fibonacci 数列中不相邻的若干个数的和,根据上面的结论,我们也容易推出如果原数能拆成
你甚至可以发现
现在的难点是如何快速维护前缀和的 Fibonacci 拆分,假设当前插入的是
- 若
u 、u+1 与u-1 当前都是0 ,直接将u 这个位置改为1 即可。 - 如果
u+1 或u-1 当前为1 ,进位过程就是将u 向右的极长0101 段缩成一个1 。 - 若
u 本就是1 ,只需要将当前插入的F_u 先完整拆开,此时刚好可以将u 前的极长0101 段的多余空位填满,进位后刚好就相当于区间平移了一下,如果0101 段的左端点为F_1 或F_2 需要特殊考虑一下。
可以直接用平衡树大力维护所有 set/map 模拟整个过程,将需要用到的下标离散化后在第二遍模拟时使用线段树维护,代码不压行甚至只有 2.5k,轻取 QOJ 最短解+最优解,但是 luogu 上肘不赢 rizynvu 大哥是怎么回事呢 qwq。
:::success[Code]
#include <bits/stdc++.h>
using namespace std;
const int N = 100010, mod = 1e9 + 7;
typedef long long ll;
int n, op, m, a[N];
vector<int> b;
map<int, int> s;
struct MAT {
ll a, b, c, d;
MAT operator * (const MAT &t) const {
return {
((a * t.a + b * t.c) % mod),
((a * t.b + b * t.d) % mod),
((c * t.a + d * t.c) % mod),
((c * t.b + d * t.d) % mod)
};
}
};
const MAT I = {1, 0, 0, 1};
vector<MAT> t;
MAT get(int d, int k) {
int x = (d - 1) / 2, y = d / 2;
return {(int)(((ll)x * (k - 1) + 1) % mod), x,
(int)(((ll)y * (k - 1) + 1) % mod), y};
}
void upd(int x, MAT v) {
if (!op) { b.push_back(x); return; }
x = lower_bound(b.begin(), b.end(), x) - b.begin() + m;
t[x] = v;
for (x >>= 1; x; x >>= 1) t[x] = t[x << 1] * t[x << 1 | 1];
}
void calc(map<int, int>::iterator it) {
if (it == s.end()) return;
int l = it->first, r = it->second, p = 0;
if (it != s.begin()) p = prev(it)->second;
upd(l, get(l - p, (r - l) / 2 + 1));
}
void change(int l, int r) {
if (l > r) return;
auto it = s.lower_bound(l);
if (it != s.end() && it->first == l) upd(l, I), it = s.erase(it);
else it = s.insert({l, r}).first, calc(it ++);
calc(it);
}
void ins(int x) {
auto it = s.upper_bound(x + 1);
if (it != s.begin() && prev(it)->second >= x - 1) {
it --;
int l = it->first, r = it->second;
change(l, r);
if (x - l & 1) {
change(l, min(x - 1, r - 2));
ins(max(x + 1, r + 1));
}
else {
change(l + 1, x - 1);
if (l > 1) ins(max(1, l - 2));
ins(r + 1);
}
}
else {
int l = x, r = x;
if (it != s.begin() && prev(it)->second == x - 2)
l = prev(it)->first, change(l, x - 2);
if (it != s.end() && it->first == x + 2)
r = it->second, change(x + 2, r);
change(l, r);
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
for (int i = 1; i <= n; i ++ ) cin >> a[i], ins(a[i]);
sort(b.begin(), b.end());
b.erase(unique(b.begin(), b.end()), b.end());
for (m = 1; m < (int)b.size(); m <<= 1) ;
t.assign(m << 1, I);
s.clear(), op = 1;
for (int i = 1; i <= n; i ++ ) {
ins(a[i]);
cout << (t[1].a + t[1].b) % mod << '\n';
}
return 0;
}
:::