「CEOI2018」斐波那契表示法

思路:维护当前数值的唯一表示法,然后根据唯一表示法来确定答案

Part1 唯一表示法

任何一个数$x$有唯一表示$P_i$,满足$x=\sum F_{P_i},P_i<P_{i+1}-1$

即不会出现相邻两项

依次插入每一个数$x$,考虑可能出现的情况

1.$x$一位以及前后为空,那么直接插入

2.$x$一位为空,且$x-1$为空,$x+1$已经出现

删除$x+1$,插入$x+2$

3.$x$一位为空,且$x+1$为空,$x-1$已经出现

删除$x-1$,插入$x+1$

4.$x$一位有

先删除$x$,然后插入$x+1,x-2$

对于操作1,2,3以及4中的$x+1$,每次操作增加$O(1)$个元素,每次递归进行删除$O(1)$个元素

操作次数为均摊$O(n)$

对于$4$操作中的$x-2$,如果$x-2$已经出现就会不断进行递归

最终的效果就是所有被操作到的$x-2,x-4,x-6\ldots$向右平移了一位

大致如此,实际情况比较复杂,要讨论x-2=0,x-2<0等等情况

用一棵平衡树维护$P_i-P_{i-1}$的值即可,4操作可以二分左边第一个>2的元素,然后进行平移

最终复杂度为$O(n\log n)$

Part2 dp求答案

令边界$P_0=0$,根据上面维护的$\delta_i=P_i-P_{i-1}$

考虑根据$\delta_i$求解答案

显然一个数$x$可以下分为$x$或者$x-1,x-2$ 或 $x-1,x-3,x-4$ 或$x-1,x-3,x-5,x-6\ldots$

且不能碰到前面的数

简单分析发现$P_i$有$\lceil \frac{\delta_i} {2}\rceil $种下分方案

然而,$P_{i-1}$如果被下分,那么$P_{i-1}$这一位会消失,变成$P_{i-1}-1$作为限制点

也就是说,$P_{i-1}$的下分会影响到$\delta_i$,使得$\delta_i\rightarrow \delta_i+1$

那么依次考虑每个$\delta_i$,令$dp_{i,f}$表示前$i$个,最后一个是否下分的方案数,可以$dp$求解

由于要动态维护,因此可以考虑用一个类似矩阵的东西来维护区间的dp情况

在平衡树中$up$维护答案即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair <int,int> Pii;
#define reg register
#define mp make_pair
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }
template <class T> inline void cmax(T &a,T b){ ((a<b)&&(a=b)); }

char IO;
template <class T=int> T rd(){
T s=0; int f=0;
while(!isdigit(IO=getchar())) f|=IO=='-';
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}

const int N=4e5+10,P=1e9+7;

int n;
struct Node{
int l,r,ma,len;
int a[2][2];
void clear(){ memset(a,0,sizeof a); }
Node(){ }
Node(int x){
l=r=ma=len=x;
rep(i,0,1) {
a[i][0]=1;
a[i][1]=(x-1+i)/2;
}
}
Node operator + (const Node _) const {
Node res; res.l=l,res.r=_.r,res.ma=max(ma,_.ma),res.len=len+_.len;
res.a[0][0]=(1ll*a[0][0]*_.a[0][0]+1ll*a[0][1]*_.a[1][0])%P;
res.a[1][0]=(1ll*a[1][0]*_.a[0][0]+1ll*a[1][1]*_.a[1][0])%P;
res.a[0][1]=(1ll*a[0][0]*_.a[0][1]+1ll*a[0][1]*_.a[1][1])%P;
res.a[1][1]=(1ll*a[1][0]*_.a[0][1]+1ll*a[1][1]*_.a[1][1])%P;
return res;
}
} s[N],val[N];

int rt,ls[N],rs[N],key[N];
void Up(int x){
s[x]=val[x];
if(ls[x]) s[x]=s[ls[x]]+s[x];
if(rs[x]) s[x]=s[x]+s[rs[x]];
}
int U(int x,int y){
if(!x||!y) return x|y;
if(key[x]<key[y]) return rs[x]=U(rs[x],y),Up(x),x;
return ls[y]=U(x,ls[y]),Up(y),y;
}

Pii Lower(int x,int len){
if(len<=0 || !x) return mp(0,x);
if(s[x].len<=len) return mp(x,0);
if(s[ls[x]].len>=len) {
Pii y=Lower(ls[x],len);
return ls[x]=y.second,Up(x),mp(y.first,x);
} else {
Pii y=Lower(rs[x],len-s[ls[x]].len-val[x].len);
return rs[x]=y.first,Up(x),mp(x,y.second);
}
}

void EraseEnd(int &x){
if(!rs[x]){ x=ls[x]; return; }
static int T[N],C;
for(int y=x;y;y=rs[y]) T[++C]=y;
rs[T[C-1]]=ls[T[C]];
drep(i,C-1,1) Up(T[i]);
C=0;
}
void AddR(int x,int y){
if(!x) return;
if(rs[x]) return AddR(rs[x],y),Up(x);
val[x]=Node(val[x].len+y),Up(x);
}
void AddL(int x,int y){
if(!x) return;
if(ls[x]) return AddL(ls[x],y),Up(x);
val[x]=Node(val[x].len+y),Up(x);
}

Pii Split(int x){
if(s[x].ma<=2) return mp(0,x);
if(val[x].ma<=2 && s[rs[x]].ma<=2) {
Pii y=Split(ls[x]);
return ls[x]=y.second,Up(x),mp(y.first,x);
} else {
Pii y=Split(rs[x]);
return rs[x]=y.first,Up(x),mp(x,y.second);
}
}
Pii Split2(int x){
if(s[x].ma<=2) return mp(x,0);
if(s[ls[x]].ma>2 || val[x].ma>2) {
Pii y=Split2(ls[x]);
return ls[x]=y.second,Up(x),mp(y.first,x);
} else {
Pii y=Split2(rs[x]);
return rs[x]=y.first,Up(x),mp(x,y.second);
}
}
int New(int x){ return key[++n]=rand(),s[n]=val[n]=Node(x),n; }

void Ins(int x){
if(x<0) return;
cmax(x,1);
if(!rt) { rt=New(x); return; }
if(s[rt].len<x-1) {
rt=U(rt,New(x-s[rt].len));
return;
}
if(s[rt].len==x-1) {
EraseEnd(rt);
return Ins(x+1);
}
Pii t=Lower(rt,x);
if(s[t.first].len!=x) {
if(x>1) {
Pii y=Lower(t.first,x-1);
if(s[y.first].len==x-1) {
AddR(y.first,s[y.second].len),rt=U(y.first,t.second);
return Ins(x+1);
}
t.first=U(y.first,y.second);
}
if(s[t.first].len==x+1) {
Pii y=Split2(t.second);
AddL(y.second,-1);
int d=s[t.first].r+s[y.first].len+1;
EraseEnd(t.first);
rt=U(U(t.first,New(d)),y.second);
return;
}
int d=s[t.first].len-x;
AddR(t.first,-d);
rt=U(U(t.first,New(d)),t.second);
return;
}
if(s[t.second].l==2) return AddL(t.second,s[t.first].r),EraseEnd(t.first),rt=U(t.first,t.second),Ins(x+1),Ins(x-2);
Pii y=Split(t.first); AddL(t.second,-1);
if(!y.first) {
if(s[y.second].l==1) {
AddL(y.second,1),rt=U(y.second,t.second);
return;
}
rt=U(U(New(1),y.second),t.second);
return;
}
if(s[y.first].len>3 && s[y.first].r==3) {
EraseEnd(y.first),AddR(y.first,2);
rt=U(U(U(y.first,New(2)),y.second),t.second);
return;
}
AddR(y.first,-2);
rt=U(U(U(y.first,New(3)),y.second),t.second);
}


int main(){
rep(kase,1,rd()) Ins(rd()),printf("%d\n",(s[rt].a[0][0]+s[rt].a[0][1])%P);
}