「CEOI2018」斐波那契表示法

「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\)维护答案即可

#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);
}

posted @ 2021-02-26 14:29  chasedeath  阅读(285)  评论(0编辑  收藏  举报